-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#! false node --ignore-this-line | ||
/* global E */ | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
Jorge-Lopes
Author
Collaborator
|
||
|
||
/// <reference types="@agoric/vats/src/core/core-eval-env"/> | ||
/// <reference types="@agoric/vats/src/core/types-ambient"/> | ||
|
||
/** | ||
* Send a payment by looking up deposit facet via namesByAddress. | ||
* | ||
* see ./post.test.js | ||
* | ||
* @param {BootstrapPowers} powers | ||
*/ | ||
const sendIt = async powers => { | ||
const addr = '{{ADDRESS}}'; | ||
const { | ||
consume: { namesByAddress, zoe }, | ||
instance: { | ||
consume: { reserve }, | ||
}, | ||
} = powers; | ||
const pf = E(zoe).getPublicFacet(reserve); | ||
const anInvitation = await E(pf).makeAddCollateralInvitation(); | ||
const addressDepositFacet = E(namesByAddress).lookup(addr, 'depositFacet'); | ||
await E(addressDepositFacet).receive(anInvitation); | ||
}; | ||
|
||
sendIt; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// Note: limit imports to node modules for portability | ||
import { parseArgs, promisify } from 'node:util'; | ||
import { execFile } from 'node:child_process'; | ||
import { writeFile, mkdtemp, rm } from 'node:fs/promises'; | ||
import { join } from 'node:path'; | ||
|
||
const options = /** @type {const} */ ({ | ||
id: { type: 'string' }, | ||
from: { type: 'string' }, | ||
bin: { type: 'string', default: '/usr/src/agoric-sdk/node_modules/.bin' }, | ||
}); | ||
|
||
const Usage = ` | ||
Try to exit an offer, reclaiming any associated payments. | ||
node exitOffer.js --id ID --from FROM [--bin PATH] | ||
Options: | ||
--id <offer id> | ||
--from <address or key name> | ||
--bin <path to agoric and agd> default: ${options.bin.default} | ||
`; | ||
|
||
const badUsage = () => { | ||
const reason = new Error(Usage); | ||
reason.name = 'USAGE'; | ||
throw reason; | ||
}; | ||
|
||
const { stringify: q } = JSON; | ||
// limited to JSON data: no remotables/promises; no undefined. | ||
const toCapData = data => ({ body: `#${q(data)}`, slots: [] }); | ||
|
||
const { entries } = Object; | ||
/** | ||
* @param {Record<string, string>} obj - e.g. { color: 'blue' } | ||
* @returns {string[]} - e.g. ['--color', 'blue'] | ||
*/ | ||
const flags = obj => | ||
entries(obj) | ||
.map(([k, v]) => [`--${k}`, v]) | ||
.flat(); | ||
|
||
const execP = promisify(execFile); | ||
|
||
const showAndRun = (file, args) => { | ||
console.log('$', file, ...args); | ||
return execP(file, args); | ||
}; | ||
|
||
const withTempFile = async (tail, fn) => { | ||
const tmpDir = await mkdtemp('offers-'); | ||
const tmpFile = join(tmpDir, tail); | ||
try { | ||
const result = await fn(tmpFile); | ||
return result; | ||
} finally { | ||
await rm(tmpDir, { recursive: true, force: true }).catch(err => | ||
console.error(err), | ||
); | ||
} | ||
}; | ||
|
||
const doAction = async (action, from) => { | ||
await withTempFile('offer.json', async tmpOffer => { | ||
await writeFile(tmpOffer, q(toCapData(action))); | ||
|
||
const out = await showAndRun('agoric', [ | ||
'wallet', | ||
...flags({ 'keyring-backend': 'test' }), | ||
'send', | ||
...flags({ offer: tmpOffer, from }), | ||
]); | ||
return out.stdout; | ||
}); | ||
}; | ||
|
||
const main = async (argv, env) => { | ||
const { values } = parseArgs({ args: argv.slice(2), options }); | ||
const { id: offerId, from, bin } = values; | ||
(offerId && from) || badUsage(); | ||
|
||
env.PATH = `${bin}:${env.PATH}`; | ||
const action = { method: 'tryExitOffer', offerId }; | ||
const out = await doAction(action, from); | ||
console.log(out); | ||
}; | ||
|
||
main(process.argv, process.env).catch(e => { | ||
if (e.name === 'USAGE' || e.code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') { | ||
console.error(e.message); | ||
} else { | ||
console.error(e); | ||
} | ||
process.exit(1); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import test from 'ava'; | ||
import { $ } from 'execa'; | ||
import { execFileSync } from 'node:child_process'; | ||
import { readFile, writeFile } from 'node:fs/promises'; | ||
import { | ||
makeAgd, | ||
waitForBlock, | ||
getUser, | ||
evalBundles, | ||
agoric, | ||
} from '@agoric/synthetic-chain'; | ||
|
||
const SUBMISSION_DIR = 'invitation-test-submission'; | ||
|
||
/** | ||
* @param {string} fileName base file name without .tjs extension | ||
* @param {Record<string, string>} replacements | ||
*/ | ||
const replaceTemplateValuesInFile = async (fileName, replacements) => { | ||
let script = await readFile(`${fileName}.tjs`, 'utf-8'); | ||
for (const [template, value] of Object.entries(replacements)) { | ||
script = script.replaceAll(`{{${template}}}`, value); | ||
} | ||
await writeFile(`${fileName}.js`, script); | ||
}; | ||
|
||
test.serial(`send invitation via namesByAddress`, async t => { | ||
const addr = await getUser('gov1'); | ||
|
||
await replaceTemplateValuesInFile(`${SUBMISSION_DIR}/send-script`, { | ||
ADDRESS: addr, | ||
}); | ||
|
||
await evalBundles(SUBMISSION_DIR); | ||
|
||
await waitForBlock(2); // enough time for invitation to arrive? | ||
const update = await agoric.follow('-lF', `:published.wallet.${addr}`); | ||
This comment has been minimized.
Sorry, something went wrong.
dckc
|
||
t.is(update.updated, 'balance'); | ||
t.notDeepEqual(update.currentAmount.value, []); | ||
t.log('balance value', update.currentAmount.value); | ||
t.log('balance brand', update.currentAmount.brand); | ||
// XXX agoric follow returns brands as strings | ||
t.regex(update.currentAmount.brand, /Invitation/); | ||
}); | ||
|
||
test.serial('exitOffer tool reclaims stuck payment', async t => { | ||
const offerId = 'bad-invitation-15'; | ||
const from = 'gov1'; | ||
|
||
const showAndExec = (file, args, opts) => { | ||
console.log('$', file, ...args); | ||
return execFileSync(file, args, opts); | ||
}; | ||
|
||
// @ts-expect-error string is not assignable to Buffer | ||
const agd = makeAgd({ execFileSync: showAndExec }).withOpts({ | ||
keyringBackend: 'test', | ||
}); | ||
|
||
const addr = await agd.lookup(from); | ||
t.log(from, 'addr', addr); | ||
|
||
const getBalance = async target => { | ||
const { balances } = await agd.query(['bank', 'balances', addr]); | ||
const { amount } = balances.find(({ denom }) => denom === target); | ||
return Number(amount); | ||
}; | ||
|
||
const before = await getBalance('uist'); | ||
t.log('uist balance before:', before); | ||
|
||
await $`node ./scripts/exitOffer.js --id ${offerId} --from ${from}`; | ||
|
||
await waitForBlock(2); | ||
const after = await getBalance('uist'); | ||
t.log('uist balance after:', after); | ||
t.true(after > before); | ||
}); |
consider using the core eval eslint environment for this
IOU a pointer
cc @turadg