Skip to content

Commit

Permalink
Merge pull request #12 from Agoric/dc-49-wf-nft
Browse files Browse the repository at this point in the history
feat: 49:smart-wallet-nft proposal
  • Loading branch information
dckc committed Nov 7, 2023
2 parents e5bccac + 182910a commit 4c55983
Show file tree
Hide file tree
Showing 8 changed files with 2,020 additions and 0 deletions.
3 changes: 3 additions & 0 deletions proposals/49:smart-wallet-nft/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "/agoric.swingset.CoreEvalProposal"
}
152 changes: 152 additions & 0 deletions proposals/49:smart-wallet-nft/core-eval-support.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// @ts-check
import {
Far,
makeMarshal,
makeTranslationTable,
} from '../../upgrade-test-scripts/lib/unmarshal.js';
import { Fail, NonNullish } from '../../upgrade-test-scripts/lib/assert.js';

// TODO: factor out ambient authority from these
// or at least allow caller to supply authority.
import { mintIST } from '../../upgrade-test-scripts/lib/econHelpers.js';
import { agoric } from '../../upgrade-test-scripts/lib/cliHelper.js';

// move to unmarshal.js?
const makeBoardUnmarshal = () => {
const synthesizeRemotable = (_slot, iface) =>
Far(iface.replace(/^Alleged: /, ''), {});

const { convertValToSlot, convertSlotToVal } = makeTranslationTable(
slot => Fail`unknown id: ${slot}`,
synthesizeRemotable,
);

return makeMarshal(convertValToSlot, convertSlotToVal);
};

export const getContractInfo = async (path, io = {}) => {
const m = makeBoardUnmarshal();
const {
agoric: { follow = agoric.follow },
prefix = 'published.',
} = io;
console.log('@@TODO: prevent agoric follow hang', prefix, path);
const txt = await follow('-lF', `:${prefix}${path}`, '-o', 'text');
const { body, slots } = JSON.parse(txt);
return m.fromCapData({ body, slots });
};

// not really core-eval related
export const testIncludes = (t, needle, haystack, label, sense = true) => {
t.log(needle, sense ? 'in' : 'not in', haystack.length, label, '?');
const check = sense ? t.deepEqual : t.notDeepEqual;
if (sense) {
t.deepEqual(
haystack.filter(c => c === needle),
[needle],
);
} else {
t.deepEqual(
haystack.filter(c => c === needle),
[],
);
}
};

/**
* @param {Record<string, string>} record - e.g. { color: 'blue' }
* @returns {string[]} - e.g. ['--color', 'blue']
*/
export const flags = record => {
return Object.entries(record)
.map(([k, v]) => [`--${k}`, v])
.flat();
};

export const txAbbr = tx => {
const { txhash, code, height, gas_used } = tx;
return { txhash, code, height, gas_used };
};

export const loadedBundleIds = swingstore => {
const ids = swingstore`SELECT bundleID FROM bundles`.map(r => r.bundleID);
return ids;
};

/**
* @param {string} cacheFn - e.g. /home/me.agoric/cache/b1-DEADBEEF.json
*/
export const bundleDetail = cacheFn => {
const fileName = NonNullish(cacheFn.split('/').at(-1));
const id = fileName.replace(/\.json$/, '');
const hash = id.replace(/^b1-/, '');
return { fileName, endoZipBase64Sha512: hash, id };
};

const importBundleCost = (bytes, price = 0.002) => {
return bytes * price;
};

/**
* @typedef {{
* bundles: string[],
* evals: { permit: string; script: string }[],
* }} ProposalInfo
*/

const myISTBalance = async (agd, addr, denom = 'uist', unit = 1_000_000) => {
const coins = await agd.query(['bank', 'balances', addr]);
const coin = coins.balances.find(a => a.denom === denom);
return Number(coin.amount) / unit;
};

/**
* @param {number} myIST
* @param {number} cost
* @param {{
* unit?: number, padding?: number, minInitialDebt?: number,
* collateralPrice: number,
* }} opts
* @returns
*/
const mintCalc = (myIST, cost, opts) => {
const {
unit = 1_000_000,
padding = 1,
minInitialDebt = 6,
collateralPrice,
} = opts;
const { round, max } = Math;
const wantMinted = max(round(cost - myIST + padding), minInitialDebt);
const giveCollateral = round(wantMinted / collateralPrice) + 1;
const sendValue = round(giveCollateral * unit);
return { wantMinted, giveCollateral, sendValue };
};

/**
*
* @param {ReturnType<typeof import('../lib/agd-lib.js').makeAgd>} agd
* @param {*} config
* @param {number} bytes total bytes
* @param {{ log: (...args: any[]) => void }} io
* @returns
*/
export const ensureISTForInstall = async (agd, config, bytes, { log }) => {
const cost = importBundleCost(bytes);
log({ totalSize: bytes, cost });
const { installer } = config;
const addr = agd.lookup(installer);
const istBalance = await myISTBalance(agd, addr);

if (istBalance > cost) {
log('balance sufficient', { istBalance, cost });
return;
}
const { sendValue, wantMinted, giveCollateral } = mintCalc(
istBalance,
cost,
config,
);
log({ wantMinted });
await mintIST(addr, sendValue, wantMinted, giveCollateral);
};
19 changes: 19 additions & 0 deletions proposals/49:smart-wallet-nft/eval.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

# Exit when any command fails
set -e

source /usr/src/upgrade-test-scripts/env_setup.sh

ls -al

yarn install --frozen-lockfile

# XXX to avoid rebuilding lower layers
# TODO clean up JS lib code so it's more independent
cp package.json yarn.lock /usr/src/upgrade-test-scripts/lib/
cd /usr/src/upgrade-test-scripts/lib/ && yarn install --frozen-lockfile
cd -

# XXX using Ava serial to script the core-eval
yarn ava upgrade-wf.test.js
18 changes: 18 additions & 0 deletions proposals/49:smart-wallet-nft/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"type": "module",
"license": "Apache-2.0",
"devDependencies": {},
"dependencies": {
"@endo/zip": "^0.2.35",
"ava": "^5.3.1",
"better-sqlite3": "^8.5.1",
"execa": "^7.2.0",
"tmp": "^0.2.1"
},
"scripts": {
"agops": "yarn --cwd /usr/src/agoric-sdk/ --silent agops"
},
"ava": {
"timeout": "30s"
}
}
18 changes: 18 additions & 0 deletions proposals/49:smart-wallet-nft/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

set -e

source /usr/src/upgrade-test-scripts/env_setup.sh

testMinChildren() {
path=$1
min=$2
line="$(agd query vstorage children $path -o jsonlines)"
ok=$(echo $line | jq ".children | length | . > $min")
test_val "$ok" "true" "$path: more than $min children"
}

# Check brand aux data for more than just vbank assets
testMinChildren published.boardAux 3

# TODO trade game asset
Loading

0 comments on commit 4c55983

Please sign in to comment.