Created
December 2, 2022 01:35
-
-
Save tgrecojs/6fcdeedd7d0cb0de2d8888f9a76d8c86 to your computer and use it in GitHub Desktop.
zoe fungible faucet contract
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @ts-check | |
/* global harden */ | |
import '@agoric/zoe/exported.js'; | |
import { AmountMath } from '@agoric/ertp'; | |
import { assert } from '@agoric/assert'; | |
import { Far } from '@endo/marshal'; | |
import { assertIssuerKeywords, swap } from '@agoric/zoe/src/contractSupport'; | |
/** | |
* This is a very simple contract that creates a new issuer and mints payments | |
* from it, in order to give an example of how that can be done. This contract | |
* sends new tokens to anyone who has an invitation. | |
* | |
* The expectation is that most contracts that want to do something similar | |
* would use the ability to mint new payments internally rather than sharing | |
* that ability widely as this one does. | |
* | |
* To pay others in tokens, the creator of the instance can make | |
* invitations for them, which when used to make an offer, will payout | |
* the specified amount of tokens. | |
* | |
* @type {ContractStartFn} | |
*/ | |
const start = async (zcf) => { | |
// Create the internal token mint for a fungible digital asset. Note | |
// that 'Tokens' is both the keyword and the allegedName. | |
const zcfMint = await zcf.makeZCFMint('Tokens'); | |
assertIssuerKeywords(zcf, ['Tokens', 'Payment']); | |
// AWAIT | |
// Now that ZCF has saved the issuer, brand, and local amountMath, they | |
// can be accessed synchronously. | |
const { issuer, brand } = zcfMint.getIssuerRecord(); | |
const { zcfSeat: sellerSeat } = zcf.makeEmptySeatKit(); | |
/** @type {OfferHandler} */ | |
const mintPayment = (seat) => { | |
const { give, want } = seat.getProposal(); | |
assert(give.Payment.value > 50n, 'Payment value must be greater than 50'); | |
/** | |
* Not sure using the want.Token.value this is giving an intellisene error: | |
* | |
* Argument of type 'bigint | SetValue | CopyTagged | CopyBagValue' is not assignable to parameter of type 'bigint'. | |
* Type 'SetValue' is not assignable to type 'bigint' | |
*/ | |
const amount = AmountMath.make(brand, want.Tokens.value); | |
// Synchronously mint and allocate amount to seat. | |
zcfMint.mintGains(harden({ Token: amount }), seat); | |
/** | |
* swap is shorthand for | |
* sellerSeat.incrementBy(seat.decrementBy({ Payment: give.Payment })) | |
*/ | |
swap(zcf, sellerSeat, seat); | |
// Exit the seat so that the user gets a payout. | |
seat.exit(); | |
// Since the user is getting the payout through Zoe, we can | |
// return anything here. Let's return some helpful instructions. | |
return 'Offer completed. You should receive a payment from Zoe'; | |
}; | |
const creatorFacet = Far('creatorFacet', { | |
// The creator of the instance can send invitations to anyone | |
// they wish to. | |
makeInvitation: () => zcf.makeInvitation(mintPayment, 'mint a payment'), | |
getTokenIssuer: () => issuer, | |
}); | |
const publicFacet = Far('publicFacet', { | |
// Make the token issuer public. Note that only the mint can | |
// make new digital assets. The issuer is ok to make public. | |
getTokenIssuer: () => issuer, | |
}); | |
// Return the creatorFacet to the creator, so they can make | |
// invitations for others to get payments of tokens. Publish the | |
// publicFacet. | |
return harden({ creatorFacet, publicFacet }); | |
}; | |
harden(start); | |
export { start }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @ts-check | |
/* eslint-disable import/order -- https://github.com/endojs/endo/issues/1235 */ | |
import { test } from './prepare-test-env-ava.js'; | |
import path from 'path'; | |
import bundleSource from '@endo/bundle-source'; | |
import { E } from '@endo/eventual-send'; | |
import { makeFakeVatAdmin } from '@agoric/zoe/tools/fakeVatAdmin.js'; | |
import { makeZoeKit } from '@agoric/zoe'; | |
import { AmountMath, makeIssuerKit } from '@agoric/ertp'; | |
const filename = new URL(import.meta.url).pathname; | |
const dirname = path.dirname(filename); | |
const contractPath = `${dirname}/../src/contract.js`; | |
const setupTestMints = () => { | |
const dollarKit = makeIssuerKit('dollars'); | |
const { | |
brand: dollarBrand, | |
issuer: dollarIssuer, | |
mint: dollarMint, | |
} = dollarKit; | |
const euroKit = makeIssuerKit('euros'); | |
const { brand: euroBrand, issuer: euroIssuer, mint: euroMint } = euroKit; | |
const dollars = (x) => AmountMath.make(dollarBrand, x); | |
const euros = (x) => AmountMath.make(euroBrand, x); | |
return { | |
dollarKit, | |
dollarBrand, | |
dollarIssuer, | |
dollarMint, | |
dollars, | |
euroKit, | |
euroBrand, | |
euroIssuer, | |
euroMint, | |
euros, | |
}; | |
}; | |
const { dollarIssuer, dollarMint, dollars } = setupTestMints(); | |
test('mint payments - called with less than the required amount', async (t) => { | |
const { zoeService } = makeZoeKit(makeFakeVatAdmin().admin); | |
const feePurse = E(zoeService).makeFeePurse(); | |
const zoe = E(zoeService).bindDefaultFeePurse(feePurse); | |
// pack the contract | |
const bundle = await bundleSource(contractPath); | |
// install the contract | |
const installation = E(zoe).install(bundle); | |
const { creatorFacet, instance } = await E(zoe).startInstance(installation, { | |
Payment: dollarIssuer, | |
}); | |
// Let's get the tokenIssuer from the contract so we can evaluate | |
// what we get as our payout | |
const publicFacet = E(zoe).getPublicFacet(instance); | |
const tokenIssuer = E(publicFacet).getTokenIssuer(); | |
const tokenBrand = await E(tokenIssuer).getBrand(); | |
const tokens = (x) => AmountMath.make(tokenBrand, x); | |
// Alice makes an invitation for Bob that will give him 1000 tokens | |
const invitation = E(creatorFacet).makeInvitation(); | |
const proposal = { | |
give: { Payment: dollars(5n) }, | |
want: { Tokens: tokens(5n) }, | |
}; | |
// Bob makes an offer using the invitation | |
const seat = E(zoe).offer(invitation, proposal, { | |
Payment: dollarMint.mintPayment(dollars(5n)), | |
}); | |
const offerResult = E(seat).getOfferResult(); | |
await t.throwsAsync(offerResult, { | |
message: 'Payment value must be greater than 50', | |
}); | |
}); | |
test('mint payments - happy path', async (t) => { | |
const { zoeService } = makeZoeKit(makeFakeVatAdmin().admin); | |
const feePurse = E(zoeService).makeFeePurse(); | |
const zoe = E(zoeService).bindDefaultFeePurse(feePurse); | |
// pack the contract | |
const bundle = await bundleSource(contractPath); | |
// install the contract | |
const installation = E(zoe).install(bundle); | |
const { creatorFacet, instance } = await E(zoe).startInstance(installation, { | |
Payment: dollarIssuer, | |
}); | |
// Let's get the tokenIssuer from the contract so we can evaluate | |
// what we get as our payout | |
const publicFacet = E(zoe).getPublicFacet(instance); | |
const tokenIssuer = E(publicFacet).getTokenIssuer(); | |
const tokenBrand = await E(tokenIssuer).getBrand(); | |
const tokens = (x) => AmountMath.make(tokenBrand, x); | |
// Alice makes an invitation for Bob that will give him 1000 tokens | |
const invitation = E(creatorFacet).makeInvitation(); | |
const proposal = { | |
give: { Payment: dollars(1000n) }, | |
want: { Tokens: tokens(1000n) }, | |
}; | |
// Bob makes an offer using the invitation | |
const seat = E(zoe).offer(invitation, proposal, { | |
Payment: dollarMint.mintPayment(dollars(1000n)), | |
}); | |
const paymentP = E(seat).getPayout('Token'); | |
const tokenPayoutAmount = await E(tokenIssuer).getAmountOf(paymentP); | |
// Bob got 1000 tokens | |
t.deepEqual(tokenPayoutAmount, tokens(1000n)); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment