Skip to content

Commit

Permalink
feat: add agoric-upgrade-18 (proposal 83)
Browse files Browse the repository at this point in the history
  • Loading branch information
mujahidkay committed Jan 13, 2025
1 parent 183440d commit 48ef8b6
Show file tree
Hide file tree
Showing 27 changed files with 7,227 additions and 0 deletions.
Binary file added .DS_Store
Binary file not shown.
Binary file added proposals/.DS_Store
Binary file not shown.
9 changes: 9 additions & 0 deletions proposals/83:upgrade-18/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Proposal to upgrade the chain software

This holds the draft proposal for agoric-upgrade-18, which will be added to
[agoric-3-proposals](https://github.com/Agoric/agoric-3-proposals) after it
passes.

The "binaries" property of `upgradeInfo` is now required since Cosmos SDK 0.46,
however it cannot be computed for an unreleased upgrade. To disable the check,
`releaseNotes` is set to `false`.
19 changes: 19 additions & 0 deletions proposals/83:upgrade-18/acceptInvites.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env node
import '@endo/init/debug.js';
import { agops, GOV1ADDR, GOV2ADDR } from '@agoric/synthetic-chain';
import { GOV4ADDR } from './agoric-tools.js';

// New GOV4 account to be added
const addresses = [GOV1ADDR, GOV2ADDR, GOV4ADDR];

await Promise.all(
addresses.map((addr, idx) =>
agops.ec('committee', '--send-from', addr, '--voter', `${idx}`),
),
);

await Promise.all(
addresses.map(addr =>
agops.ec('charter', '--send-from', addr, '--name', 'econCommitteeCharter'),
),
);
20 changes: 20 additions & 0 deletions proposals/83:upgrade-18/addGov4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import '@endo/init/debug.js';
import { execFileSync } from 'node:child_process';
import { makeAgd } from './synthetic-chain-excerpt.js';
import { GOV4ADDR } from './agoric-tools.js';

const agd = makeAgd({ execFileSync }).withOpts({ keyringBackend: 'test' });

agd.keys.add(
'gov4',
'smile unveil sketch gaze length bulb goddess street case exact table fetch robust chronic power choice endorse toward pledge dish access sad illegal dance',
);

agd.tx(
['swingset', 'provision-one', 'faucet_provision', GOV4ADDR, 'SMART_WALLET'],
{
chainId: 'agoriclocal',
from: 'validator',
yes: true,
},
);
207 changes: 207 additions & 0 deletions proposals/83:upgrade-18/agd-tools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import {
agd,
agops,
agopsLocation,
CHAINID,
executeCommand,
executeOffer,
GOV1ADDR,
GOV2ADDR,
GOV3ADDR,
VALIDATORADDR,
} from '@agoric/synthetic-chain';

const ORACLE_ADDRESSES = [GOV1ADDR, GOV2ADDR, GOV3ADDR];

export const BID_OFFER_ID = 'bid-vaultUpgrade-test3';

const queryVstorage = path =>
agd.query('vstorage', 'data', '--output', 'json', path);

// XXX use endo/marshal?
const getQuoteBody = async path => {
const queryOut = await queryVstorage(path);

const body = JSON.parse(JSON.parse(queryOut.value).values[0]);
return JSON.parse(body.body.substring(1));
};

export const getOracleInstance = async price => {
const instanceRec = await queryVstorage(`published.agoricNames.instance`);

const value = JSON.parse(instanceRec.value);
const body = JSON.parse(value.values.at(-1));

const feeds = JSON.parse(body.body.substring(1));
const feedName = `${price}-USD price feed`;

const key = Object.keys(feeds).find(k => feeds[k][0] === feedName);
if (key) {
return body.slots[key];
}
return null;
};

export const checkForOracle = async (t, name) => {
const instance = await getOracleInstance(name);
t.truthy(instance);
};

export const registerOraclesForBrand = async (brandIn, oraclesByBrand) => {
await null;
const promiseArray = [];

const oraclesWithID = oraclesByBrand.get(brandIn);
for (const oracle of oraclesWithID) {
const { address, offerId } = oracle;
promiseArray.push(
executeOffer(
address,
agops.oracle('accept', '--offerId', offerId, `--pair ${brandIn}.USD`),
),
);
}

return Promise.all(promiseArray);
};

/**
* Generate a consistent map of oracleIDs for a brand that can be used to
* register oracles or to push prices. The baseID changes each time new
* invitations are sent/accepted, and need to be maintained as constants in
* scripts that use the oracles. Each oracleAddress and brand needs a unique
* offerId, so we create recoverable IDs using the brandName and oracle id,
* mixed with the upgrade at which the invitations were accepted.
*
* @param {string} baseId
* @param {string} brandName
*/
const addOraclesForBrand = (baseId, brandName) => {
const oraclesWithID = [];
for (let i = 0; i < ORACLE_ADDRESSES.length; i += 1) {
const oracleAddress = ORACLE_ADDRESSES[i];
const offerId = `${brandName}.${baseId}.${i}`;
oraclesWithID.push({ address: oracleAddress, offerId });
}
return oraclesWithID;
};

export const addPreexistingOracles = async (brandIn, oraclesByBrand) => {
await null;

const oraclesWithID = [];
for (let i = 0; i < ORACLE_ADDRESSES.length; i += 1) {
const oracleAddress = ORACLE_ADDRESSES[i];

const path = `published.wallet.${oracleAddress}.current`;
const wallet = await getQuoteBody(path);
const idToInvitation = wallet.offerToUsedInvitation.find(([k]) => {
return !Number.isNaN(k[0]);
});
if (idToInvitation) {
oraclesWithID.push({
address: oracleAddress,
offerId: idToInvitation[0],
});
} else {
console.log('AGD addO skip', oraclesWithID);
}
}

oraclesByBrand.set(brandIn, oraclesWithID);
};

/**
* Generate a consistent map of oracleIDs and brands that can be used to
* register oracles or to push prices. The baseID changes each time new
* invitations are sent/accepted, and need to be maintained as constants in
* scripts that use these records to push prices.
*
* @param {string} baseId
* @param {string[]} brandNames
*/
export const generateOracleMap = (baseId, brandNames) => {
const oraclesByBrand = new Map();
for (const brandName of brandNames) {
const oraclesWithID = addOraclesForBrand(baseId, brandName);
oraclesByBrand.set(brandName, oraclesWithID);
}
return oraclesByBrand;
};

export const pushPrices = (price, brandIn, oraclesByBrand) => {
const promiseArray = [];

for (const oracle of oraclesByBrand.get(brandIn)) {
promiseArray.push(
executeOffer(
oracle.address,
agops.oracle(
'pushPriceRound',
'--price',
price,
'--oracleAdminAcceptOfferId',
oracle.offerId,
),
),
);
}

return Promise.all(promiseArray);
};

export const getPriceQuote = async price => {
const path = `published.priceFeed.${price}-USD_price_feed`;
const body = await getQuoteBody(path);
return body.amountOut.value;
};

export const agopsInter = (...params) => {
const newParams = ['inter', ...params];
return executeCommand(agopsLocation, newParams);
};

export const createBid = (price, addr, offerId) => {
return agopsInter(
'bid',
'by-price',
`--price ${price}`,
`--give 1.0IST`,
'--from',
addr,
'--keyring-backend test',
`--offer-id ${offerId}`,
);
};

export const getLiveOffers = async addr => {
const path = `published.wallet.${addr}.current`;
const body = await getQuoteBody(path);
return body.liveOffers;
};

export const getAuctionCollateral = async index => {
const path = `published.auction.book${index}`;
const body = await getQuoteBody(path);
return body.collateralAvailable.value;
};

export const getVaultPrices = async index => {
const path = `published.vaultFactory.managers.manager${index}.quotes`;
const body = await getQuoteBody(path);
return body.quoteAmount;
};

export const bankSend = (addr, wanted) => {
const chain = ['--chain-id', CHAINID];
const from = ['--from', VALIDATORADDR];
const testKeyring = ['--keyring-backend', 'test'];
const noise = [...from, ...chain, ...testKeyring, '--yes'];

return agd.tx('bank', 'send', VALIDATORADDR, addr, wanted, ...noise);
};

export const getProvisionPoolMetrics = async () => {
const path = `published.provisionPool.metrics`;
return getQuoteBody(path);
};
133 changes: 133 additions & 0 deletions proposals/83:upgrade-18/agoric-tools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import assert from 'node:assert';
import {
queryVstorage,
agops,
agoric,
executeOffer,
} from '@agoric/synthetic-chain';
import { makeMarshal, Remotable } from '@endo/marshal';

export const GOV4ADDR = 'agoric1c9gyu460lu70rtcdp95vummd6032psmpdx7wdy';

const slotToRemotable = (_slotId, iface = 'Remotable') =>
Remotable(iface, undefined, {
getBoardId: () => _slotId,
});

// /** @param {BoardRemote | object} val */
const boardValToSlot = val => {
if ('getBoardId' in val) {
return val.getBoardId();
}
throw Error(`unknown obj in boardSlottingMarshaller.valToSlot ${val}`);
};

const boardSlottingMarshaller = slotToVal => {
return makeMarshal(boardValToSlot, slotToVal, {
serializeBodyFormat: 'smallcaps',
});
};

export const marshaller = boardSlottingMarshaller(slotToRemotable);

export const queryVstorageFormatted = async (path, index = -1) => {
const data = await queryVstorage(path);

const formattedData = JSON.parse(data.value);
const formattedDataAtIndex = JSON.parse(formattedData.values.at(index));
return marshaller.fromCapData(formattedDataAtIndex);
};

export const generateVaultDirectorParamChange = async (
previousOfferId,
voteDur,
params,
paramsPath,
) => {
const voteDurSec = BigInt(voteDur);
const toSec = ms => BigInt(Math.round(ms / 1000));

const id = `propose-${Date.now()}`;
const deadline = toSec(Date.now()) + voteDurSec;

const zip = (xs, ys) => xs.map((x, i) => [x, ys[i]]);
// KLUDGE: partial deconstruction of smallCaps values
const fromSmallCapsEntries = txt => {
const { body, slots } = JSON.parse(txt);
const theEntries = zip(JSON.parse(body.slice(1)), slots).map(
([[name, ref], boardID]) => {
const iface = ref.replace(/^\$\d+\./, '');
return [name, { iface, boardID }];
},
);
return Object.fromEntries(theEntries);
};

const slots = []; // XXX global mutable state
const smallCaps = {
Nat: n => `+${n}`,
// XXX mutates obj
ref: obj => {
if (obj.ix) return obj.ix;
const ix = slots.length;
slots.push(obj.boardID);
obj.ix = `$${ix}.Alleged: ${obj.iface}`;
return obj.ix;
},
};

await null;
const instance = fromSmallCapsEntries(
await agoric.follow('-lF', ':published.agoricNames.instance', '-o', 'text'),
);
assert(instance.VaultFactory);

const body = {
method: 'executeOffer',
offer: {
id,
invitationSpec: {
invitationMakerName: 'VoteOnParamChange',
previousOffer: previousOfferId,
source: 'continuing',
},
offerArgs: {
deadline: smallCaps.Nat(deadline),
instance: smallCaps.ref(instance.VaultFactory),
params,
path: paramsPath,
},
proposal: {},
},
};

const capData = { body: `#${JSON.stringify(body)}`, slots };
return JSON.stringify(capData);
};

export const proposeVaultDirectorParamChange = async (
address,
params,
path,
) => {
const charterAcceptOfferId = await agops.ec(
'find-continuing-id',
'--for',
`${'charter\\ member\\ invitation'}`,
'--from',
address,
);

return executeOffer(
address,
generateVaultDirectorParamChange(charterAcceptOfferId, 30, params, path),
);
};

export const voteForNewParams = (accounts, position) => {
return Promise.all(
accounts.map(account =>
agops.ec('vote', '--forPosition', position, '--send-from', account),
),
);
};
Loading

0 comments on commit 48ef8b6

Please sign in to comment.