Proposal details

#64 Deploy Crabble rc-1

Passed

Bytepitch has introduced Crabble, a smart-contract for short-term lending of NFTs on the Agoric chain, which intends to automate several key features to make the system easy, safe, and fast for the users. Beyond functioning solely as an NFT rental platform, Crabble aims to serve as a foundational infrastructure accessible to other projects to leverage rental features into their ecosystems and further enhance the utility and flexibility of NFTs across various markets and applications. Link of community forum: https://community.agoric.com/t/add-smart-contract-for-crabble-dapp/564/1

Submit time: 16 Dec 2023, 03:52

Details

Voting period

100.00%
16 Dec 2023, 03:5219 Dec 2023, 03:52

Core Eval Proposal

  • Title

    Deploy Crabble rc-1

  • Type

    CoreEvalProposal

  • Data
    {
    "evals":[
    0:{
    "jsonPermits":"{ "consume": { "chainTimerService": true, "governedContractKits": true, "diagnostics": true, "zoe": true }, "produce": { "startCrabbleGovernedUpgradable": true } }"
    "jsCode":"/* eslint-disable no-shadow */ /** * @file Add support for starting governed contracts to the Agoric bootstrap / * CoreEval "promise space". */ // @ts-check // XMPORT: { E } from '@endo/far'; // XMPORT { CONTRACT_ELECTORATE, ParamTypes } from '@agoric/governance'; const CONTRACT_ELECTORATE = 'Electorate'; const ParamTypes = /** @type {const} */ ({ INVITATION: 'invitation', }); const { fromEntries, entries } = Object; /** @type { <T extends Record<string, ERef<any>>>(obj: T) => Promise<{ [K in keyof T]: Awaited<T[K]>}> } */ const allValues = async (obj) => { const es = await Promise.all( entries(obj).map(async ([k, v]) => [k, await v]), ); return fromEntries(es); }; const logger = (message) => { console.log('[PRODUCE_UPGRADABLE_STARTER]', message); }; /** * @template SF @typedef * {XMPORT('@agoric/zoe/src/zoeService/utils').StartResult<SF>} * StartResult<SF> */ /** * @typedef {StartResult< * typeof XMPORT('@agoric/governance/src/committee.js').start * >} CommitteeStart */ /** * Like startGovernedInstance but with parameterized committeeCreatorFacet * * @template {GovernableStartFn} SF * @param {{ * zoe: ERef<ZoeService>; * governedContractInstallation: ERef<Installation<SF>>; * issuerKeywordRecord?: IssuerKeywordRecord; * terms: Record<string, unknown>; * privateArgs: any; // TODO: connect with Installation type * label: string; * }} zoeArgs * @param {{ * governedParams: Record<string, unknown>; * timer: ERef<XMPORT('@agoric/time/src/types').TimerService>; * contractGovernor: ERef<Installation>; * governorTerms: Record<string, unknown>; * committeeCreatorFacet: CommitteeStart['creatorFacet']; * }} govArgs * @returns {Promise<GovernanceFacetKit<SF>>} */ const startCrabbleGovernedInstance = async ( { zoe, governedContractInstallation, issuerKeywordRecord, terms, privateArgs, label, }, { governedParams, timer, contractGovernor, governorTerms, committeeCreatorFacet, }, ) => { const logger = (message) => { console.log('[START_GOVERNED_INSTANCE]', message); }; logger('Getting poser invitation...'); const poserInvitationP = E(committeeCreatorFacet).getPoserInvitation(); const [initialPoserInvitation, electorateInvitationAmount] = await Promise.all([ poserInvitationP, E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP), ]); logger('Fulfilling governor terms...'); const fullGovernorTerms = await allValues({ timer, governedContractInstallation, governed: { terms: { ...terms, governedParams: { [CONTRACT_ELECTORATE]: { type: ParamTypes.INVITATION, value: electorateInvitationAmount, }, ...governedParams, }, }, issuerKeywordRecord, label, }, ...governorTerms, }); logger('Starting governor...'); const governorFacets = await E(zoe).startInstance( contractGovernor, {}, fullGovernorTerms, harden({ economicCommitteeCreatorFacet: committeeCreatorFacet, governed: await allValues({ ...privateArgs, initialPoserInvitation, }), }), `${label}-governor`, ); logger('Getting facets...'); const [instance, publicFacet, creatorFacet, adminFacet] = await Promise.all([ E(governorFacets.creatorFacet).getInstance(), E(governorFacets.creatorFacet).getPublicFacet(), E(governorFacets.creatorFacet).getCreatorFacet(), E(governorFacets.creatorFacet).getAdminFacet(), ]); /** @type {GovernanceFacetKit<SF>} */ const facets = harden({ instance, publicFacet, governor: governorFacets.instance, creatorFacet, adminFacet, governorCreatorFacet: governorFacets.creatorFacet, governorAdminFacet: governorFacets.adminFacet, }); return facets; }; /** * @param {BootstrapSpace & { * produce: { * startCrabbleGovernedUpgradable: Producer<Function>; * }; * }} powers */ const produceStartCrabbleGovernedUpgradable = async ({ consume: { chainTimerService, governedContractKits, diagnostics, zoe }, produce, // startCrabbleGovernedUpgradable }) => { logger('Producing governed upgradable...'); /** * @template {GovernableStartFn} SF * @param {StartGovernedUpgradableOpts<SF> & { * committeeCreatorFacet: CommitteeStart['creatorFacet']; * contractGovernor: ERef<Installation>; * governorTerms: Record<string, unknown>; * }} opts */ const startGovernedUpgradable = async ({ installation, issuerKeywordRecord, committeeCreatorFacet, contractGovernor, governorTerms, governedParams, terms, privateArgs, label, }) => { const contractKits = await governedContractKits; logger('Init startCrabbleGovernedInstance...'); const facets = await startCrabbleGovernedInstance( { zoe, governedContractInstallation: installation, issuerKeywordRecord, terms, privateArgs, label, }, { governedParams, timer: chainTimerService, contractGovernor, committeeCreatorFacet, governorTerms, }, ); logger('Updating contractKits...'); const kit = harden({ ...facets, label }); contractKits.init(facets.instance, kit); logger('Updating diagnostics...'); await E(diagnostics).savePrivateArgs(kit.instance, privateArgs); await E(diagnostics).savePrivateArgs(kit.governor, { committeeCreatorFacet: await committeeCreatorFacet, }); return facets; }; produce.startCrabbleGovernedUpgradable.resolve( harden(startGovernedUpgradable), ); logger('Done.'); }; harden(produceStartCrabbleGovernedUpgradable); // script completion value produceStartCrabbleGovernedUpgradable; "
    }
    1:{
    "jsonPermits":"{ "consume": { "board": true, "chainStorage": true, "chainTimerService": true, "agoricNames": true, "zoe": true, "namesByAddressAdmin": true, "startCrabbleGovernedUpgradable": true, "startUpgradable": true }, "installation": { "consume": { "committee": true } }, "instance": { "produce": { "Crabble": true, "CrabbleCommittee": true, "CrabbleGovernor": true } } }"
    "jsCode":"// @ts-check // XMPORT: { E } from '@endo/far'; const fail = (msg) => { throw Error(msg); }; const { fromEntries, keys, values } = Object; /** @type {<X, Y>(xs: X[], ys: Y[]) => [X, Y][]} */ const zip = (xs, ys) => harden(xs.map((x, i) => [x, ys[+i]])); /** * @type {<T extends Record<string, ERef<any>>>( * obj: T, * ) => Promise<{ [K in keyof T]: Awaited<T[K]> }>} */ const allValues = async (obj) => { const resolved = await Promise.all(values(obj)); // @ts-expect-error cast return harden(fromEntries(zip(keys(obj), resolved))); }; const logger = (...args) => { console.log('[CRABBLE_CORE_EVAL]', ...args); }; /** * @template T * @typedef {{ * resolve: (v: ERef<T>) => void; * reject: (r: unknown) => void; * reset: (reason?: unknown) => void; * }} ProducerX<T> */ /** * @param {{ * consume: { * agoricNames: ERef<XMPORT('@agoric/vats').NameHub>; * board: ERef<XMPORT('@agoric/vats').Board>; * startUpgradable: Promise<Function>; * namesByAddressAdmin: ERef<XMPORT('@agoric/vats').NameAdmin>; * }; * instance: { produce: Record<'CrabbleCommittee', ProducerX<Instance>> } * }} powers * @param {*} config * @param {ERef<StorageNode>} crabbleNode */ const startCommittee = async ( { consume: { board, // namesByAddress should suffice, but... // https://github.com/Agoric/agoric-sdk/issues/8113 namesByAddressAdmin, startUpgradable, }, installation: { consume: { committee: committeeInstallationP }, }, instance: { produce: produceInstance }, }, config, crabbleNode, ) => { const committeeSize = 3; const committeeName = "CrabbleCommittee"; const members = ["agoric1ag5a8lhn00h4u9h2shpfpjpaq6v4kku54zk69m","agoric140y0mqnq7ng5vvxxwpfe67988e5vqar9whg309","agoric1wqfu6hu5q2qtey9jtjapaae4df9zd492z4448k"]; logger('Getting nameHubs, depositFacets...'); const getDepositFacet = async (address) => { const hub = E(E(namesByAddressAdmin).lookupAdmin(address)).readonly(); return E(hub).lookup('depositFacet'); }; const memberDeposits = await Promise.all(members.map(getDepositFacet)); logger('Gathering info...'); const { committeeInstallation, marshaller, committeeNode } = await allValues({ committeeInstallation: committeeInstallationP, marshaller: E(board).getPublishingMarshaller(), committeeNode: E(crabbleNode).makeChildNode('committee'), }); logger('Starting committee...'); const committeeKit = await E(startUpgradable)({ installation: committeeInstallation, terms: { committeeSize, committeeName }, privateArgs: { storageNode: committeeNode, marshaller }, label: committeeName, }); logger({ committeeKit }); logger('Updating agoricNames with committee instance...'); produceInstance.CrabbleCommittee.resolve(committeeKit.instance); logger('Getting the member and voter invitations...'); const voterInvitations = await E( committeeKit.creatorFacet, ).getVoterInvitations(); logger('Sending committeeinvitations...'); await Promise.all( zip(memberDeposits, voterInvitations).map(([depositFacet, invitation]) => E(depositFacet).receive(invitation), ), ); logger('Done.'); return { committeeCreatorFacet: committeeKit.creatorFacet, memberDeposits }; }; /** * * @param {{ * consume: { * zoe: Promise<ZoeService>; * board: ERef<XMPORT('@agoric/vats').Board>, * startCrabbleGovernedUpgradable: Promise<Function>, * chainTimerService: ERef<XMPORT('@agoric/time/src/types').TimerService>; * agoricNames: ERef<XMPORT('@agoric/vats').NameHub>; * }, * instance: { produce: Record<'Crabble' | 'CrabbleGovernor', ProducerX<Instance>>} * }} powers * @param {*} config * @param {ERef<StorageNode>} crabbleNode * @param {Promise<{ * committeeCreatorFacet: ERef<any>; * memberDeposits: ERef<DepositFacet>[] * }>} committeeInfoP */ const startCrabble = async (powers, config, crabbleNode, committeeInfoP) => { const contractBundleID = "b1-3af8183538129ce433d368dc0bdb6082733fc0fa6449c18d5f212f104f971d8ea8b033074067b807bffd5eb2f17821c5e2e9b26ba312795368ae8e7446606b85"; const governorBundleID = "b1-0bfb8a189cda652bc43c488e079e6362870e49039a247156f9430f4f0dfa054970f01c108b79c476baddc8b814546ad88578358452fe2919d11fdcc224bbe4b3"; const { consume: { board, startCrabbleGovernedUpgradable, zoe: zoeI, // only used for installation, not for startInstance chainTimerService, agoricNames: agoricNamesP, }, instance: { produce: produceInstance }, } = powers; logger('Gathering info...'); const { contractInstallation, governorInstallation, binaryVoteCounterInstallation, committeeInstallation, marshaller, timer, info: { committeeCreatorFacet, memberDeposits }, agoricNames, } = await allValues({ contractInstallation: E(zoeI).installBundleID(contractBundleID), governorInstallation: E(zoeI).installBundleID(governorBundleID), binaryVoteCounterInstallation: E(agoricNamesP).lookup( 'installation', 'binaryVoteCounter', ), committeeInstallation: E(agoricNamesP).lookup('installation', 'committee'), marshaller: E(board).getPublishingMarshaller(), timer: chainTimerService, info: committeeInfoP, agoricNames: agoricNamesP, }); logger({ contractInstallation, binaryVoteCounterInstallation, committeeInstallation, marshaller, crabbleNode, }); logger('---Starting Crabble with governor---'); const crabbleTerms = { agoricNames, }; const crabblePrivateArgs = { storageNode: crabbleNode, marshaller, timer, }; logger({ crabbleTerms, crabblePrivateArgs, }); logger('Deeply fulfill governorTerms...'); const governorTerms = harden({ timer, // ISSUE: TIMER IN TERMS governedContractInstallation: contractInstallation, binaryVoteCounterInstallation, }); logger({ governorTerms, }); logger('Starting governor, governed...'); const kit = await E(startCrabbleGovernedUpgradable)({ installation: contractInstallation, committeeCreatorFacet, contractGovernor: governorInstallation, governorTerms, terms: crabbleTerms, privateArgs: crabblePrivateArgs, label: 'Crabble', }); logger({ kit, }); logger('Updating agoricNames with instances...'); produceInstance.Crabble.resolve(kit.instance); produceInstance.CrabbleGovernor.resolve(kit.governor); logger('Sending member invitations...'); await Promise.all( memberDeposits.map(async (df) => { const inv = await E( kit.governorCreatorFacet, ).makeCommitteeMemberInvitation(); return E(df).receive(inv); }), ); logger('Done.'); }; harden(startCrabble); const start = async (powers, config) => { const { consume: { chainStorage }, } = powers; const crabbleNode = await E(chainStorage).makeChildNode('crabble'); const committeeInfo = startCommittee(powers, config, crabbleNode); await startCrabble(powers, config, crabbleNode, committeeInfo); }; harden(start); start; "
    }
    ]
    "@type":"/agoric.swingset.CoreEvalProposal"
    }