Skip to content

Commit

Permalink
feat: donate RUN from the bootstrap payment on each provision
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfig committed May 1, 2021
1 parent 92a0a44 commit 43c5db5
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 52 deletions.
5 changes: 4 additions & 1 deletion golang/cosmos/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ type cosmosInitAction struct {
ChainID string `json:"chainID"`
BootstrapAddress string `json:"bootstrapAddress"`
BootstrapValue string `json:"bootstrapValue"`
DonationValue string `json:"donationValue"`
}

// MakeCodecs constructs the *std.Codec and *codec.LegacyAmino instances used by
Expand All @@ -527,7 +528,7 @@ func (app *GaiaApp) MustInitController(ctx sdk.Context) {
app.controllerInited = true

/*
FIXME: Get this from genesis!
FIXME: Get these from genesis!
- name: mallory
type: local
address: agoric1z8rldx498tf49p5ze04jhakyumkh7vyxku7e0p
Expand All @@ -542,6 +543,7 @@ func (app *GaiaApp) MustInitController(ctx sdk.Context) {
os.Exit(1)
}
bootstrapValue := "50000000000"
donationValue := "5000000"

// Begin initializing the controller here.
action := &cosmosInitAction{
Expand All @@ -552,6 +554,7 @@ func (app *GaiaApp) MustInitController(ctx sdk.Context) {
ChainID: ctx.ChainID(),
BootstrapAddress: bootstrapAddr.String(),
BootstrapValue: bootstrapValue,
DonationValue: donationValue,
}
bz, err := json.Marshal(action)
if err == nil {
Expand Down
2 changes: 1 addition & 1 deletion packages/dapp-svelte-wallet/api/src/internal-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
* @property {(path: string[], val: T) => void} addPath
* @property {(petname: Petname, val: T) => void} renamePetname
* @property {(petname: Petname) => void} deletePetname
* @property {(petname: Petname, val: T) => void} suggestPetname
* @property {(petname: Petname, val: T) => Petname} suggestPetname
* @property {string} kind
*/

Expand Down
37 changes: 32 additions & 5 deletions packages/dapp-svelte-wallet/api/src/lib-wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -637,17 +637,26 @@ export function makeWallet({
return `instance ${q(petname)} successfully added to wallet`;
};

const makeEmptyPurse = async (
/**
* This function is marked internal and unsafe because it permits importing a
* shared purse (which we don't necessarily trust).
*
* @param {Petname} brandPetname
* @param {Petname} petnameForPurse
* @param {boolean} defaultAutoDeposit
* @param {(issuer: Issuer) => ERef<Purse>} importPurse
*/
const internalUnsafeImportPurse = async (
brandPetname,
petnameForPurse,
defaultAutoDeposit = false,
purseMaker = issuer => E(issuer).makeEmptyPurse(),
defaultAutoDeposit,
importPurse,
) => {
const brand = brandMapping.petnameToVal.get(brandPetname);
const { issuer } = brandTable.getByBrand(brand);

/** @type {Purse} */
const purse = await purseMaker(issuer);
const purse = await importPurse(issuer);

purseToBrand.init(purse, brand);
petnameForPurse = purseMapping.suggestPetname(petnameForPurse, purse);
Expand Down Expand Up @@ -675,6 +684,19 @@ export function makeWallet({
});
};

// This function is exposed to the walletAdmin.
const makeEmptyPurse = (
brandPetname,
petnameForPurse,
defaultAutoDeposit = false,
) =>
internalUnsafeImportPurse(
brandPetname,
petnameForPurse,
defaultAutoDeposit,
issuer => E(issuer).makeEmptyPurse(),
);

async function deposit(pursePetname, payment) {
const purse = purseMapping.petnameToVal.get(pursePetname);
return E(purse).deposit(payment);
Expand Down Expand Up @@ -1526,13 +1548,18 @@ export function makeWallet({
zoeInvitePurse = wallet.getPurse(ZOE_INVITE_PURSE_PETNAME);
};

// Importing assets as virtual purses from the bank is a highly-trusted path.
// We don't want to expose this mechanism to the user, in case they shoot
// themselves in the foot with it by importing an asset/virtual purse they
// don't really trust.
const importBankAssets = async bank => {
observeIteration(E(bank).getAssetSubscription(), {
async updateState({ proposedName, issuerName, issuer, brand }) {
try {
await addIssuer(issuerName, issuer);
const purse = await E(bank).getPurse(brand);
await wallet.makeEmptyPurse(
// We can import this purse, because we trust the bank.
await internalUnsafeImportPurse(
issuerName,
proposedName,
true,
Expand Down
73 changes: 47 additions & 26 deletions packages/vats/src/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ const QUOTE_INTERVAL = 5 * 60;

const BASIS_POINTS_DENOM = 10000n;

// TODO: decide on initial value to be distributed. This would give
// 20,000 users each 1 display unit of RUN
const BOOTSTRAP_PAYMENT_VALUE = 20000n * 10n ** 6n;

console.debug(`loading bootstrap.js`);

// Used for coordinating on an index in comms for the provisioning service
Expand All @@ -48,7 +44,6 @@ function makeVattpFrom(vats) {
}

export function buildRootObject(vatPowers, vatParameters) {
console.error('%%%HAVE vatParameters', vatParameters);
const { D } = vatPowers;
async function setupCommandDevice(httpVat, cmdDevice, roles) {
await E(httpVat).setCommandDevice(cmdDevice, roles);
Expand All @@ -63,6 +58,9 @@ export function buildRootObject(vatPowers, vatParameters) {
vatAdminSvc,
noFakeCurrencies,
) {
/** @type {ERef<ReturnType<import('./vat-bank')['buildRootObject']>>} */
const bankVat = vats.bank;

// Create singleton instances.
const [
bankManager,
Expand All @@ -73,7 +71,7 @@ export function buildRootObject(vatPowers, vatParameters) {
zoe,
{ priceAuthority, adminFacet: priceAuthorityAdmin },
] = await Promise.all([
bridgeManager && E(vats.bank).makeBankManager(bridgeManager),
bridgeManager ? E(bankVat).makeBankManager(bridgeManager) : undefined,
E(vats.sharing).getSharingService(),
E(vats.registrar).getSharedRegistrar(),
E(vats.board).getBoard(),
Expand All @@ -95,7 +93,7 @@ export function buildRootObject(vatPowers, vatParameters) {
nameAdmin: pegasusConnectionsAdmin,
} = makeNameHubKit();

async function installEconomy() {
async function installEconomy(bootstrapPaymentValue) {
// Create a mapping from all the nameHubs we create to their corresponding
// nameAdmin.
/** @type {Store<NameHub, NameAdmin>} */
Expand Down Expand Up @@ -124,7 +122,7 @@ export function buildRootObject(vatPowers, vatParameters) {
nameAdmins,
priceAuthority,
zoe,
bootstrapPaymentValue: BOOTSTRAP_PAYMENT_VALUE,
bootstrapPaymentValue,
}),
installPegasusOnChain({
agoricNames,
Expand All @@ -137,8 +135,13 @@ export function buildRootObject(vatPowers, vatParameters) {
return treasuryCreator;
}

const { bootstrapAddress, donationValue = '0', bootstrapValue = '0' } =
(vatParameters && vatParameters.argv && vatParameters.argv.bootMsg) || {};
const bootstrapPaymentValue = BigInt(bootstrapValue);
const donationPaymentValue = BigInt(donationValue);

// Now we can bootstrap the economy!
const treasuryCreator = await installEconomy();
const treasuryCreator = await installEconomy(bootstrapPaymentValue);

const [
centralIssuer,
Expand All @@ -152,34 +155,35 @@ export function buildRootObject(vatPowers, vatParameters) {
E(agoricNames).lookup('instance', 'Pegasus'),
]);

async function payThePiper(bootMsg) {
if (!bankManager || !bootMsg) {
return;
}
// We just transfer the bootstrapValue in central tokens to the low-level
// bootstrapAddress.
const { bootstrapAddress, bootstrapValue } = bootMsg;
if (!bootstrapAddress || !bootstrapValue) {
/** @type {undefined | import('@agoric/eventual-send').EOnly<Purse>} */
let centralBootstrapPurse;

// We just transfer the bootstrapValue in central tokens to the low-level
// bootstrapAddress.
async function depositCentralBootstrapPayment() {
if (!bankManager || !bootstrapAddress || !bootstrapPaymentValue) {
return;
}
await E(bankManager).addAsset(
'urun',
CENTRAL_ISSUER_NAME,
'Agoric RUN currency',
harden({ issuer: centralIssuer, brand: centralBrand }),
);
const bank = await E(bankManager).getBankForAddress(bootstrapAddress);
const pmt = await E(treasuryCreator).getBootstrapPayment(
amountMath.make(BigInt(bootstrapValue), centralBrand),
amountMath.make(bootstrapPaymentValue, centralBrand),
);
const purse = E(bank).getPurse(centralBrand);
await E(purse).deposit(pmt);
centralBootstrapPurse = E(bank).getPurse(centralBrand);
await E(centralBootstrapPurse).deposit(pmt);
}
false &&
(await payThePiper(
vatParameters && vatParameters.argv && vatParameters.argv.bootMsg,
));
await depositCentralBootstrapPayment();

/** @type {[string, import('./issuers').IssuerInitializationRecord]} */
const CENTRAL_ISSUER_ENTRY = [
CENTRAL_ISSUER_NAME,
{
issuer: centralIssuer,
defaultPurses: [['Agoric RUN currency', 0]],
tradesGivenCentral: [[1, 1]],
},
];
Expand Down Expand Up @@ -214,7 +218,7 @@ export function buildRootObject(vatPowers, vatParameters) {
issuerName,
harden({ ...record, brand, issuer }),
);
if (!record.bankDenom || !record.bankPurse) {
if (!record.bankDenom || !record.bankPurse || !bankManager) {
return issuer;
}

Expand Down Expand Up @@ -510,6 +514,23 @@ export function buildRootObject(vatPowers, vatParameters) {

const bank = await (bankManager &&
E(bankManager).getBankForAddress(address));

/** @param {NatValue} value */
async function donateCentralFromBootstrap(value) {
if (!bank || !centralBootstrapPurse) {
return;
}
const provisionAmount = amountMath.make(value, centralBrand);
const centralUserPurse = E(bank).getPurse(centralBrand);
const centralPayment = await E(centralBootstrapPurse).withdraw(
provisionAmount,
);
await E(centralUserPurse).deposit(
/** @type {Payment} */ (centralPayment),
);
}
await donateCentralFromBootstrap(donationPaymentValue);

const bundle = harden({
...additionalPowers,
agoricNames,
Expand Down
44 changes: 25 additions & 19 deletions packages/vats/src/vat-bank.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,34 @@ const makePurseController = (
},
});

/**
* @typedef {Object} AssetIssuerKit
* @property {Mint} [mint]
* @property {Issuer} issuer
* @property {Brand} brand
*/

/**
* @typedef {AssetIssuerKit & { denom: string }} AssetRecord
*/

/**
* @typedef {Object} Bank
* @property {() => Subscription<AssetDescriptor>}
* getAssetSubscription Returns assets as they are added to the bank
* @property {(brand: Brand) => VirtualPurse} getPurse Find any existing vpurse (keyed by address and brand) or create a
* new one.
*/

export function buildRootObject(_vatPowers) {
return Far('bankMaker', {
/**
* @param {import('./bridge').BridgeManager} bridgeMgr
*/
async makeBankManager(bridgeMgr) {
/**
* @typedef {Object} BrandRecord
* @property {ERef<Issuer>} issuer
* @property {ERef<Mint>} mint
*/

const bankCall = obj => E(bridgeMgr).toBridge('bank', obj);

/** @type {Store<Brand, IssuerKit & { denom: string }>} */
/** @type {Store<Brand, AssetRecord>} */
const brandToAssetRecord = makeStore('brand');
/** @type {Store<string, Store<string, (amount: any) => void>>} */
const denomToAddressUpdater = makeStore('denom');
Expand Down Expand Up @@ -121,6 +134,7 @@ export function buildRootObject(_vatPowers) {
publication: assetPublication,
} = makeSubscriptionKit();

/** @type {Store<string, Bank>} */
const addressToBank = makeStore('address');
return Far('bankManager', {
/**
Expand All @@ -137,7 +151,7 @@ export function buildRootObject(_vatPowers) {
* @param {string} denom lower-level denomination string
* @param {string} issuerName
* @param {string} proposedName
* @param {IssuerKit} kit ERTP issuer kit (mint, brand, issuer)
* @param {AssetIssuerKit} kit ERTP issuer kit (mint, brand, issuer)
*/
async addAsset(denom, issuerName, proposedName, kit) {
assert.typeof(denom, 'string');
Expand Down Expand Up @@ -169,6 +183,7 @@ export function buildRootObject(_vatPowers) {
* Create a new personal bank interface for a given address.
*
* @param {string} address lower-level bank account address
* @returns {Bank}
*/
getBankForAddress(address) {
assert.typeof(address, 'string');
Expand All @@ -178,21 +193,12 @@ export function buildRootObject(_vatPowers) {

/** @type {Store<Brand, VirtualPurse>} */
const brandToVPurse = makeStore('brand');

/** @type {Bank} */
const bank = Far('bank', {
/**
* Returns assets as they are added to the bank.
*
* @returns {Subscription<AssetDescriptor>}
*/
getAssetSubscription() {
return harden(assetSubscription);
},
/**
* Find any existing vpurse (keyed by address and brand) or create a
* new one.
*
* @param {Brand} brand
*/
async getPurse(brand) {
if (brandToVPurse.has(brand)) {
return brandToVPurse.get(brand);
Expand Down

0 comments on commit 43c5db5

Please sign in to comment.