Git Product home page Git Product logo

agoric-sdk-liquidation-visibility's Introduction

banner-1500x500

Agoric Platform SDK

This repository contains most of the packages that make up the upper layers of the Agoric platform, with the endo repository providing the lower layers. If you want to build on top of this platform, you don't need these repositories: instead you should follow our instructions for getting started with the Agoric SDK.

But if you are improving the platform itself, these are the repositories to use.

Prerequisites

  • Git
  • Node.js LTS (version 16.13.0 or higher)
    • we generally support the latest LTS release: use nvm to keep your local system up-to-date
  • Yarn (npm install -g yarn)
  • gcc-10 or newer, or a compiler with __has_builtin()

Any version of Yarn will do: the .yarnrc file should ensure that all commands use the specific checked-in version of Yarn (stored in .yarn/releases/), which we can update later with PRs in conjunction with any necessary compatibility fixes to our package.json files.

Building on Apple Silicon and Newer Architectures

Some dependencies may not be prebuilt for Apple Silicon and other newer architectures, so it may be necessary to build these dependencies from source and install that package’s native dependencies with your package manager (e.g. Homebrew).

Currently these dependencies are:

Additionally, if your package manager utilizes a non-standard include path, you may also need to export the following environment variable before running the commands in the Build section.

export CPLUS_INCLUDE_PATH=/opt/homebrew/include

Finally, you will need the native build toolchain installed to build these items from source.

xcode-select --install

Build

From a new checkout of this repository, run:

yarn install
yarn build

When the yarn install is done, the top-level node_modules/ will contain all the shared dependencies, and each subproject's node_modules/ should contain only the dependencies that are unique to that subproject (e.g. when the version installed at the top level does not meet the subproject's constraints). Our goal is to remove all the unique-to-a-subproject deps.

When one subproject depends upon another, node_modules/ will contain a symlink to the subproject (e.g. ERTP depends upon marshal, so node_modules/@endo/marshal is a symlink to packages/marshal).

Run yarn workspaces info to get a report on which subprojects (aka "workspaces") depend upon which others. The mismatchedWorkspaceDependencies section tells us when symlinks could not be used (generally because e.g. ERTP wants [email protected], but packages/marshal/package.json says it's actually 0.2.0). We want to get rid of all mismatched dependencies.

The yarn build step generates kernel bundles.

Test

To run all unit tests (in all packages):

  • yarn test (from the top-level)

To run the unit tests of just a single package (e.g. eventual-send):

  • cd packages/eventual-send
  • yarn test

Run the larger demo

Visit https://docs.agoric.com for getting started instructions.

TL;DR:

  • yarn link-cli ~/bin/agoric
  • cd ~
  • agoric init foo
  • cd foo
  • agoric install
  • agoric start

Then browse to http://localhost:8000

Edit Loop

  • modify something in e.g. zoe/
  • run yarn build (at the top level or in zoe/)
  • re-run tests or agoric start --reset
  • repeat

Doing a yarn build in zoe creates the "contract facet bundle", a single file that rolls up all the Zoe contract vat sources. This bundle file is needed by all zoe contracts before they can invoke zoe~.install(...). If you don't run yarn build, then changes to the Zoe contract facet will be ignored.

Development Standards

  • All work should happen on branches. Single-commit branches can land on trunk without a separate merge, but multi-commit branches should have a separate merge commit. The merge commit subject should mention which packages were modified (e.g. (SwingSet,cosmic-swingset) merge 123-fix-persistence)
  • Keep the history tidy. Avoid overlapping branches. Rebase when necessary.
  • All work should have an Issue. All branches names should include the issue number as a prefix (e.g. 123-description). Use "Labels" on the Issues to mark which packages are affected.
  • Add user-visible changes to a new file in the changelogs/ directory, named after the Issue number. See the README in those directories for instructions.
  • Unless the issue spans multiple packages, each branch should only modify a single package.
  • Releases should be made as according to MAINTAINERS.md.

agoric-sdk-liquidation-visibility's People

Contributors

arirubinstein avatar chris-hibbert avatar davidbruant avatar dckc avatar dependabot[bot] avatar dtribble avatar erights avatar fudco avatar gamarin2 avatar gibson042 avatar iomekam avatar jackzampolin avatar jfparadis avatar jimlarson avatar joeabbey avatar katelynsills avatar kriskowal avatar luqipan avatar mergify[bot] avatar mhofman avatar michaelfig avatar okwme avatar raphdev avatar samsiegart avatar sunnya97 avatar toliaqat avatar turadg avatar tyg avatar warner avatar zarutian avatar

agoric-sdk-liquidation-visibility's Issues

When making timestampStorageNode result in a rejected promise, the liquidationVisibilityWriters will be the auctionSchedule

Describe the bug

Assume as a premise that at makeLiquidationRecorderKits, we force a promise rejection of the makeChildNode used to create the timestampStorageNode.

makeChildNode(name, childNodeOptions = {}) {
  if (name === '3600') {
    console.log('Log: MOCK makeChildNode ... REJECT :');
    return Promise.reject();
  }

When the liquidateVaults is executed, and it tries to write the PreAuction State, it will fail because the liquidationVisibilityWriters will have the value expected for auctionSchedule

          await null;
          const [
            { userSeatPromise, deposited },
            liquidationVisibilityWriters,
            auctionSchedule,
          ] = (
            await Promise.allSettled([
              makeDeposit,
              helper.makeLiquidationVisibilityWriters(timestamp),
              schedulesP,
            ])
          )
            .filter(result => result.status === 'fulfilled')
            // @ts-expect-error
            .map(result => result.value);

          void helper.writeLiqVisibility(liquidationVisibilityWriters, [
            ['writePreAuction', vaultData],
          ]);

The error will be triggered because the if (!liquidationVisibilityWriters) condition will never be true.

        async writeLiqVisibility(liquidationVisibilityWriters, writes) {
          console.log('WRITES', writes);
          if (!liquidationVisibilityWriters) {
            trace(
              'writeLiqVisibility',
              `Error: liquidationVisibilityWriters is ${liquidationVisibilityWriters}`,
            );
            return;
          }

          for (const [methodName, params] of writes) {
            trace('DEBUG', methodName, params);
            void liquidationVisibilityWriters[methodName](params);
          }
        },

These logs show the value of liquidationVisibilityWriters in this scenario

Log: MOCK makeChildNode ... 3600
Log: MOCK makeChildNode ... REJECT :
----- VM.14  18 Object [Alleged: aEth brand] {} chargeAllVaults {
  updateTime: { absValue: 3600n, timerBrand: Object [Alleged: timerBrand] {} }
}
WRITES [
  [
    'writePreAuction',
    Object [Alleged: scalar MapStore of "key"] {
      has: [Function: has],
      get: [Function: get],
      init: [Function: init],
      set: [Function: set],
      delete: [Function: delete],
      addAll: [Function: addAll],
      keys: [Function: keys],
      values: [Function: values],
      entries: [Function: entries],
      snapshot: [Function: snapshot],
      getSize: [Function: getSize],
      clear: [Function: clear]
    }
  ]
]
LOG: liquidationVisibilityWriters {
  liveAuctionSchedule: {
    startTime: { timerBrand: Object [Alleged: timerBrand] {}, absValue: 3610n },
    endTime: { timerBrand: Object [Alleged: timerBrand] {}, absValue: 3614n },
    steps: 2n,
    endRate: 6500n,
    startDelay: { relValue: 10n, timerBrand: Object [Alleged: timerBrand] {} },
    clockStep: { relValue: 2n, timerBrand: Object [Alleged: timerBrand] {} },
    lockTime: { timerBrand: Object [Alleged: timerBrand] {}, absValue: 3607n }
  },
  nextAuctionSchedule: {
    startTime: { timerBrand: Object [Alleged: timerBrand] {}, absValue: 7210n },
    endTime: { timerBrand: Object [Alleged: timerBrand] {}, absValue: 7214n },
    steps: 2n,
    endRate: 6500n,
    startDelay: { relValue: 10n, timerBrand: Object [Alleged: timerBrand] {} },
    clockStep: { relValue: 2n, timerBrand: Object [Alleged: timerBrand] {} },
    lockTime: { timerBrand: Object [Alleged: timerBrand] {}, absValue: 7207n }
  }
}

Error message:

  ✘ [fail]: liq-reject-child-node
    ℹ ----- VisibilityTools.9  2 pa {
        governorInstance: Object @Alleged: InstanceHandle {},
        priceAuthority: true,
        vaultFactory: Object @Alleged: VaultDirector machine {},
        vfPublic: Object @Alleged: VaultDirector public {},
      }

  Unhandled rejection in test/liquidationVisibility/test-liquidationVisibility.js

  TypeError: liquidationVisibilityWriters[methodName] is not a function
    at Object.writeLiqVisibility (.../inter-protocol/src/vaultFactory/vaultManager.js:786:46)
    at Object.liquidateVaults (.../inter-protocol/src/vaultFactory/vaultManager.js:1405:13)

  › Object.writeLiqVisibility (.../inter-protocol/src/vaultFactory/vaultManager.js:786:46)
  › Object.liquidateVaults (.../inter-protocol/src/vaultFactory/vaultManager.js:1405:13)

SES_UNHANDLED_REJECTION: (TypeError#2)
TypeError#2: liquidationVisibilityWriters[methodName] is not a function
  at Object.writeLiqVisibility (.../inter-protocol/src/vaultFactory/vaultManager.js:786:46)
  at Object.liquidateVaults (.../inter-protocol/src/vaultFactory/vaultManager.js:1405:13)

  ─

  liq-reject-child-node
  Value is the same as:

  0

  › packages/inter-protocol/test/liquidationVisibility/test-liquidationVisibility.js:1121:5

  ─

  1 test failed
  1 unhandled rejection

To Reproduce

Run the test 'liq-result-scenario-1' from the this commit 575302c

After adding a new asset to Vaults, how is the collateral payment extracted from smart wallet when opening a new vault?

Context

The purpose of this question is to understand the process of adding a new asset type to the vaults and then opening a new vault of that collateral. More specifically, in the bootstrap test environment, how are the smart wallet provisioned with the required amount of collateral to open a new vault?

As a guide for this question, lets use the test-liquidation-1.ts as an example, more specifically the last 2 tests related to the collateral STARS.

Let's follow the execution flow of adding a new collateral to the vaults, and then open a new vault.

Add new collateral.

When the ensureVaultCollateral('STARS', t) function is executed in the test environment, the addSTARsCollateral function will be invoked. This function aims to build and deploy a proposal to add a new collateral to the vaults.

const addSTARsCollateral = async (t) => {
  const { controller, buildProposal } = t.context;

  t.log('building proposal');
  const proposal = await buildProposal(
    '@agoric/builders/scripts/inter-protocol/add-STARS.js',
  );

ref: liquidation.ts

The starsVaultProposalBuilder import the proposal builder and pass the interchainAssetOptions. Note that the denom is declared and not the issuerBoardId. This will be relevant later.

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */
export const starsVaultProposalBuilder = async powers => {
  return vaultProposalBuilder(powers, {
    interchainAssetOptions: {
      // Values for the Stargaze token on Osmosis
      denom:
        'ibc/987C17B11ABC2B20019178ACE62929FE9840202CE79498E29FE8E5CB02B7C0A4',
      decimalPlaces: 6,
      keyword: 'STARS',
      oracleBrand: 'STARS',
      proposedName: 'STARS',
    },
  });
};

ref: add-STARS.js

The defaultProposalBuilder imports the getManifestForAddAssetToVault and pass the required arguments.

ref: add-collateral-core.js

The getManifestForAddAssetToVault will assert which manifest should be executed based on the interchainAssetOptions, as mentioned above, and in this case, the publishInterchainAssetFromBank is the one that we expect to run.

NOTE: at the produce section we see that bankMints and vBankKits and being produced. I cannot find other references to these capabilities.

export const getManifestForAddAssetToVault = (
  { restoreRef },
  {
    debtLimitValue,
    interestRateValue,
    interchainAssetOptions,
    scaledPriceAuthorityRef,
  },
) => {
  const publishIssuerFromBoardId =
    typeof interchainAssetOptions.issuerBoardId === 'string';
  const publishIssuerFromBank =
    !publishIssuerFromBoardId &&
    typeof interchainAssetOptions.denom === 'string';
  return {
    manifest: {
      
			...

      ...(publishIssuerFromBank && {
        [publishInterchainAssetFromBank.name]: {
          consume: {
            bankManager: true,
            agoricNamesAdmin: true,
            reserveKit: true,
            startUpgradable: true,
          },
          produce: { bankMints: true, vBankKits: true },
          installation: {
            consume: { mintHolder: true },
          },
        },

ref: addAssetToVault.js

The publishInterchainAssetFromBank will create a new issuerKit with the mintHolder contract and pass that kit as argument to the addAsset method of bankManager

export const publishInterchainAssetFromBank = async (
  {
    consume: { bankManager, agoricNamesAdmin, reserveKit, startUpgradable },
    installation: {
      consume: { mintHolder },
    },
  },
  { options: { interchainAssetOptions } },
) => {
  const {
    denom,
    decimalPlaces,
    keyword,
    issuerName = keyword,
    proposedName = keyword,
  } = interchainAssetOptions;

  const terms = {
    keyword: issuerName, // "keyword" is a misnomer in mintHolder terms
    assetKind: AssetKind.NAT,
    displayInfo: {
      decimalPlaces,
      assetKind: AssetKind.NAT,
    },
  };

  const { creatorFacet: mint, publicFacet: issuer } = await E(startUpgradable)({
    installation: mintHolder,
    label: issuerName,
    privateArgs: undefined,
    terms,
  });

  const brand = await E(issuer).getBrand();
  const kit = { mint, issuer, brand };

  await E(E.get(reserveKit).creatorFacet).addIssuer(issuer, keyword);

  await Promise.all([
    E(E(agoricNamesAdmin).lookupAdmin('issuer')).update(issuerName, issuer),
    E(E(agoricNamesAdmin).lookupAdmin('brand')).update(issuerName, brand),
    E(bankManager).addAsset(denom, issuerName, proposedName, kit),
  ]);
};

ref: addAssetToVault.js

The addAsset will create an escrow purse from the kit.issuer provided above and deposits a payment into it.
Note: I cannot identify where was that payment created, and if I try to print its amount it will trigger an error. Although, when I print the purse balance after the deposit, it will show 0 STARS.

The addAsset will then declare privateAssetRecord and toPublish objects. Where they will be used to initialise the brandToAssetRecord and brandToAssetDescriptor respectively.

Note: the brandToAssetRecord is used at the getPurse method of bank.

Finally, if there's a nameAdmin, it updates with the settled issuer identity.

      async addAsset(denom, issuerName, proposedName, kit) {
        const {
          assetPublisher,
          brandToAssetDescriptor,
          brandToAssetRecord,
          denomToAddressUpdater,
          nameAdmin,
        } = this.state;

        const brand = await kit.brand;
        const assetKind = await E(kit.issuer).getAssetKind();

        // Create an escrow purse for this asset, seeded with the payment.
        const escrowPurse = E(kit.issuer).makeEmptyPurse();
        const payment = await kit.payment;
        await (payment && E(escrowPurse).deposit(payment));
  
        const [privateAssetRecord, toPublish] = await deeplyFulfilledObject(
          harden([
            {
              escrowPurse,
              issuer: kit.issuer,
              mint: kit.mint,
              denom,
              brand,
            },
            {
              brand,
              denom,
              issuerName,
              issuer: kit.issuer,
              proposedName,
            },
          ]),
        );
        brandToAssetRecord.init(brand, privateAssetRecord);
        denomToAddressUpdater.init(
          denom,
          detachedZone.mapStore('addressToUpdater'),
        );
        brandToAssetDescriptor.init(brand, toPublish);
        assetPublisher.publish(toPublish);

        if (!nameAdmin) {
          return;
        }
        // publish settled issuer identity
        void Promise.all([kit.issuer, E(kit.brand).getDisplayInfo()]).then(
          ([issuer, displayInfo]) =>
            E(nameAdmin).update(
              denom,
              /** @type {AssetInfo} */ (
                harden({
                  brand,
                  issuer,
                  issuerName,
                  denom,
                  proposedName,
                  displayInfo,
                })
              ),
            ),
        );
      },

ref: vat-bank.js

Open new vault

At the test environment, the makeLiquidationTestKit export a method called setupVaults. This method will use the consumed walletFactoryDriver to create a SmartWallet with the address agoric1minter.

Then It will use the executeOfferMaker method of the smart wallet to give the instructions to open a new vault.

Note: for now, lets focus on how did this wallet got the funds to open the vault.

 const setupVaults = async (
    collateralBrandKey: string,
    managerIndex: number,
    setup: LiquidationSetup,
    base: number = 0,
  ) => {
    await setupStartingState({
      collateralBrandKey,
      managerIndex,
      price: setup.price.starting,
    });

    const minter =
      await walletFactoryDriver.provideSmartWallet('agoric1minter');

    for (let i = 0; i < setup.vaults.length; i += 1) {
      const offerId = `open-${collateralBrandKey}-vault${base + i}`;
      await minter.executeOfferMaker(Offers.vaults.OpenVault, {
        offerId,
        collateralBrandKey,
        wantMinted: setup.vaults[i].ist,
        giveCollateral: setup.vaults[i].atom,
      });
      t.like(minter.getLatestUpdateRecord(), {
        updated: 'offerStatus',
        status: { id: offerId, numWantsSatisfied: 1 },
      });
    }

ref: liquidation.ts

The provideSmartWallet at drivers.ts a bank will be created, passing as argument to getBankForAddress the wallet address.

The getBankForAddress will create a new bank, initialise it in the addressToBank and return it.

    async provideSmartWallet(
      walletAddress: string,
    ): Promise<ReturnType<typeof makeWalletDriver>> {
      const bank = await EV(bankManager).getBankForAddress(walletAddress);
      return EV(walletFactoryStartResult.creatorFacet)
        .provideSmartWallet(walletAddress, bank, namesByAddressAdmin)
        .then(([walletPresence, isNew]) =>
          makeWalletDriver(walletAddress, walletPresence, isNew),
        );
    },

ref: drivers.ts

The method above will call provideSmartWallet from the walletFactory, which will use the bank object to make a new smart wallet and return it.

     provideSmartWallet(address, bank, namesByAddressAdmin) {
        let isNew = false;

        /** @type {(address: string) => Promise<import('./smartWallet.js').SmartWallet>} */
        const maker = async _address => {
          const invitationPurse = await E(invitationIssuer).makeEmptyPurse();
          const walletStorageNode = E(storageNode).makeChildNode(address);
          const wallet = await makeSmartWallet(
            harden({ address, walletStorageNode, bank, invitationPurse }),
          );

          // An await here would deadlock with invitePSMCommitteeMembers
          void publishDepositFacet(address, wallet, namesByAddressAdmin);

          isNew = true;
          return wallet;
        };

        const finisher = walletReviver
          ? async (_address, _wallet) => {
              const isRevive = await E(walletReviver).ackWallet(address);
              isNew = !isRevive;
            }
          : undefined;

        return provider
          .provideAsync(address, maker, finisher)
          .then(w => [w, isNew]);
      },

ref: walletFactory.js

At makeSmartWallet, the makeWalletWithResolvedStorageNodes is an ExoClassKit that holds all the facets belonging to the smart wallet and has stored in its state, the bank passed before.

Note: this function will only return the self facet.

  const makeSmartWallet = async uniqueWithoutChildNodes => {
    const [walletStorageNode, currentStorageNode] = await Promise.all([
      uniqueWithoutChildNodes.walletStorageNode,
      E(uniqueWithoutChildNodes.walletStorageNode).makeChildNode('current'),
    ]);

    return makeWalletWithResolvedStorageNodes(
      harden({
        ...uniqueWithoutChildNodes,
        currentStorageNode,
        walletStorageNode,
      }),
    ).self;
  };

ref: smartWallet.js

At provideSmartWallet, after making the new smart wallet, the publishDepositFacet is called. This method will use the namesByAddressAdmin passed by the drivers to associate the wallet address with its deposit facet.

Note: did not identified yet the purpose of this step

export const publishDepositFacet = async (
  address,
  wallet,
  namesByAddressAdmin,
) => {
  const { nameAdmin: myAddressNameAdmin } = await E(
    namesByAddressAdmin,
  ).provideChild(address, [WalletName.depositFacet]);

  return E(myAddressNameAdmin).default(
    WalletName.depositFacet,
    wallet.getDepositFacet(),
  );
};

ref: walletFactory.js

At the provideSmartWallet of drivers.js, after receiving the new smart wallet, the makeWalletDriver method will wrap the wallet around new methods and return it.

Note: the only method that seems to be executed in this flow is executeOfferMaker

getPurse

Wrong `endTime` in vstorage

Describe the bug

vaultManager uses schedule.nextAuctionSchedule.endTime when producing data for vstorage's auctionResult node.

To Reproduce

cd agoric-sdk/packages/inter-protocol
yarn test test/liquidationVisibility/test-liquidationVisibility.js -m liq-result-scenario-2

Expected behavior

vaultManager should use schedule.liveAuctionSchedule.endTime since we expect the current auction's end time and not the next one's.

Steps required for landing the PR

Context

We are planning to use this issue as the main place for discussing steps we to complete before landing the PR.

Assumptions

We assume all points have been met down below before discussing the next steps to land the PR.

  • Code review completed
  • Sufficient unit test coverage has been reached
  • vstorage usage has been approved by the OpCo
  • No linting or type errors in the code

Topics

Problem When Setting Up Integration Tests

Problem Definition

So far we haven't considered the case where the version of the agoric-sdk will probably be different to one in the a3p image. The OpCo stated that our work will probably be on top of agoric-upgrade-15 , so we based our work on tmp-upgrade-15, and the latest a3p image is built on top of agoric-upgrade-13. This situation creates an asymmetry when we try to make our features work on the latest a3p image as our work includes contribution to some packages of the sdk that are other than inter-protocol package.

This situation creates two main problems;

  1. What are we going to do with the code that is outside of inter-protocol package?
    Given that the piece of code we're talking about here is just a helper function it's possible to move to vaultManager and not import when using it. However, we believe that it's a useful utility method that other devs might also need.

  2. What version of agoric-sdk should we build our proposals on?
    Depending on version of the agoric-sdk, endoZipBase64Sha512 of our proposal bundles changes. This makes it harder to build a deterministic test environment where we're certain of what we're testing is actually what've built.

    • What makes it even more complicated is that our post eval test cases require that we make a vaults liquidation happen in order to test our features. To create a controlled liquidation we must control the timer auctioneer and vaultFactory uses. Both auctioneer and vaultFactory accepts timerService from their terms which cannot be updated in a later incarnation without any source code changes.

Solution

Problem 1

Move allValuesSettled method to vaultManager and open a separate PR where it's added to @agoric/internal.

Problem 2

We don't know if our code work on agoric-upgrade-13 as is. We should rebase the code in develop onto agoric-upgeade-13 and run our tests.
* If they work as is, bundle the code from agoric-upgrade-13
* If not => ?

Luckily, there were no conflicts when rebasing develop onto upstream/agoric-upgrade-13 and all tests passed.

Problem 2.b

Perform a separate upgrade just for post eval tests where the source code replaces the timerService with the one we're passing from privateArgs over the one sent through terms.

Proposal: error while extracting context for action

Describe the bug

When submitting a proposal to update the VaultFactory contract, the core-eval fails with the following error startVaultFactory not in getManifestForVaultFactory.
Based on this I imagine I have made some mistake when building the manifest or even in the proposal builder script. Although I am not being able to spot the bug.

Links to source code:

To Reproduce

Steps to reproduce the behavior:

  1. Go to the following agoric-sdk forked branch jorge/proposal
  2. Launch agoric local chain and client
cd packages/agoric-cli/test
./start-local-chain.sh
cd agoric-sdk/packages/cosmic-swingset
make SOLO_COINS='13000000ubld,12345000000000uist,1122000000ibc/toyusdc' scenario2-run-client
  1. Generate bundles
cd packages/builders/scripts/inter-protocol/liquidationVisibility
yarn bundle-source --cache-js $(pwd)/bundles  /<path to sdk>/packages/inter-protocol/src/vaultFactory/vaultFactory.js vaultFactory
  1. Submit proposal
cd packages/builders/scripts/inter-protocol/liquidationVisibility
make local-proposal
  1. Show Error
Chain logs:
2024/02/07 15:44:42 error while extracting context for action {{"CORE_EVAL" 'ñ' '�'} [{"{\n  \"consume\": {\n    \"board\": \"board\",\n    \"chainStorage\": true,\n    \"diagnostics\": true,\n    \"feeMintAccess\": \"zoe\",\n    \"chainTimerService\": \"timer\",\n    \"zoe\": \"zoe\",\n    \"priceAuthority\": \"priceAuthority\",\n    \"economicCommitteeCreatorFacet\": \"economicCommittee\",\n    \"reserveKit\": \"reserve\",\n    \"auctioneerKit\": \"auction\",\n    \"agoricNamesAdmin\": \"makeCoreProposalBehavior\",\n    \"vatAdminSvc\": \"makeCoreProposalBehavior\"\n  },\n  \"produce\": {\n    \"vaultFactoryKit\": \"VaultFactory\"\n  },\n  \"brand\": {\n    \"consume\": {\n      \"IST\": \"zoe\"\n    }\n  },\n  \"oracleBrand\": {\n    \"consume\": {\n      \"USD\": true\n    }\n  },\n  \"installation\": {\n    \"consume\": {\n      \"contractGovernor\": \"zoe\",\n      \"VaultFactory\": \"zoe\"\n    },\n    \"produce\": \"makeCoreProposalBehavior\"\n  },\n  \"instance\": {\n    \"consume\": {\n      \"reserve\": \"reserve\",\n      \"auctioneer\": \"auction\"\n    },\n    \"produce\": {\n      \"VaultFactory\": \"VaultFactory\",\n      \"VaultFactoryGovernor\": \"VaultFactoryGovernor\"\n    }\n  },\n  \"evaluateBundleCap\": \"makeCoreProposalBehavior\",\n  \"modules\": {\n    \"utils\": {\n      \"runModuleBehaviors\": \"makeCoreProposalBehavior\"\n    }\n  }\n}" "// This is generated by writeCoreProposal; please edit!\n/* eslint-disable */\n\nconst manifestBundleRef = {bundleID:\"b1-aaefe6af1d3f97e01c9e92129a855db38f17736c1050b87a80b0606f44adca8dc92be334f5b18b4af6f97effb673b85e3744fa9b0851b3f73015d0351e6a5c60\"};\nconst getManifestCall = harden([\n  \"getManifestForVaultFactory\",\n  {\n    installKeys: {\n      vaultFactory: {\n        bundleID: \"b1-0549e2e790529d9ce158b65a56051332f7750453cde89395ace96fc6d384dcda41014fd1a43353f639865d726e709b12d8a132d25cb3ab4e8ee267256ed356be\",\n      },\n    },\n    vaultFactoryControllerAddress: undefined,\n  },\n]);\nconst customManifest = {\n  startVaultFactory: {\n    brand: {\n      consume: {\n        IST: \"zoe\",\n      },\n    },\n    consume: {\n      auctioneerKit: \"auction\",\n      board: \"board\",\n      chainStorage: true,\n      chainTimerService: \"timer\",\n      diagnostics: true,\n      economicCommitteeCreatorFacet: \"economicCommittee\",\n      feeMintAccess: \"zoe\",\n      priceAuthority: \"priceAuthority\",\n      reserveKit: \"reserve\",\n      zoe: \"zoe\",\n    },\n    installation: {\n      consume: {\n        VaultFactory: \"zoe\",\n        contractGovernor: \"zoe\",\n      },\n    },\n    instance: {\n      consume: {\n        auctioneer: \"auction\",\n        reserve: \"reserve\",\n      },\n      produce: {\n        VaultFactory: \"VaultFactory\",\n        VaultFactoryGovernor: \"VaultFactoryGovernor\",\n      },\n    },\n    oracleBrand: {\n      consume: {\n        USD: true,\n      },\n    },\n    produce: {\n      vaultFactoryKit: \"VaultFactory\",\n    },\n  },\n};\n\n// Make a behavior function and \"export\" it by way of script completion value.\n// It is constructed by an anonymous invocation to ensure the absence of a global binding\n// for makeCoreProposalBehavior, which may not be necessary but preserves behavior pre-dating\n// https://github.com/Agoric/agoric-sdk/pull/8712 .\nconst behavior = (({\n  manifestBundleRef,\n  getManifestCall: [manifestGetterName, ...manifestGetterArgs],\n  customManifest,\n  E,\n  log = console.info,\n  customRestoreRef,\n}) => {\n  const { entries, fromEntries } = Object;\n\n  /**\n   * Given an object whose properties may be promise-valued, return a promise\n   * for an analogous object in which each such value has been replaced with its\n   * fulfillment.\n   * This is a non-recursive form of endo `deeplyFulfilled`.\n   *\n   * @template T\n   * @param {{[K in keyof T]: (T[K] | Promise<T[K]>)}} obj\n   * @returns {Promise<T>}\n   */\n  const shallowlyFulfilled = async obj => {\n    if (!obj) {\n      return obj;\n    }\n    const awaitedEntries = await Promise.all(\n      entries(obj).map(async ([key, valueP]) => {\n        const value = await valueP;\n        return [key, value];\n      }),\n    );\n    return fromEntries(awaitedEntries);\n  };\n\n  const makeRestoreRef = (vatAdminSvc, zoe) => {\n    /** @type {(ref: import\\('./externalTypes.js').ManifestBundleRef) => Promise<Installation<unknown>>} */\n    const defaultRestoreRef = async bundleRef => {\n      // extract-proposal.js creates these records, and bundleName is\n      // the optional name under which the bundle was installed into\n      // config.bundles\n      const bundleIdP =\n        'bundleName' in bundleRef\n          ? E(vatAdminSvc).getBundleIDByName(bundleRef.bundleName)\n          : bundleRef.bundleID;\n      const bundleID = await bundleIdP;\n      const label = bundleID.slice(0, 8);\n      return E(zoe).installBundleID(bundleID, label);\n    };\n    return defaultRestoreRef;\n  };\n\n  /** @param {ChainBootstrapSpace & BootstrapPowers & { evaluateBundleCap: any }} powers */\n  const coreProposalBehavior = async powers => {\n    // NOTE: `powers` is expected to match or be a superset of the above `permits` export,\n    // which should therefore be kept in sync with this deconstruction code.\n    // HOWEVER, do note that this function is invoked with at least the *union* of powers\n    // required by individual moduleBehaviors declared by the manifest getter, which is\n    // necessary so it can use `runModuleBehaviors` to provide the appropriate subset to\n    // each one (see ./writeCoreProposal.js).\n    // Handle `powers` with the requisite care.\n    const {\n      consume: { vatAdminSvc, zoe, agoricNamesAdmin },\n      evaluateBundleCap,\n      installation: { produce: produceInstallations },\n      modules: {\n        utils: { runModuleBehaviors },\n      },\n    } = powers;\n\n    // Get the on-chain installation containing the manifest and behaviors.\n    log('evaluateBundleCap', {\n      manifestBundleRef,\n      manifestGetterName,\n      vatAdminSvc,\n    });\n    let bcapP;\n    if ('bundleName' in manifestBundleRef) {\n      bcapP = E(vatAdminSvc).getNamedBundleCap(manifestBundleRef.bundleName);\n    } else if ('bundleID' in manifestBundleRef) {\n      bcapP = E(vatAdminSvc).getBundleCap(manifestBundleRef.bundleID);\n    } else {\n      const keys = Reflect.ownKeys(manifestBundleRef).map(key =>\n        typeof key === 'string' ? JSON.stringify(key) : String(key),\n      );\n      const keysStr = `[${keys.join(', ')}]`;\n      throw Error(\n        `bundleRef must have own bundleName or bundleID, missing in ${keysStr}`,\n      );\n    }\n    const bundleCap = await bcapP;\n\n    const proposalNS = await evaluateBundleCap(bundleCap);\n\n    // Get the manifest and its metadata.\n    log('execute', {\n      manifestGetterName,\n      bundleExports: Object.keys(proposalNS),\n    });\n    const restoreRef = customRestoreRef || makeRestoreRef(vatAdminSvc, zoe);\n    const {\n      manifest,\n      options: rawOptions,\n      installations: rawInstallations,\n    } = await proposalNS[manifestGetterName](\n      harden({ restoreRef }),\n      ...manifestGetterArgs,\n    );\n\n    // Await promises in the returned options and installations records.\n    const [options, installations] = await Promise.all(\n      [rawOptions, rawInstallations].map(shallowlyFulfilled),\n    );\n\n    // Publish the installations for our dependencies.\n    const installationEntries = entries(installations || {});\n    if (installationEntries.length > 0) {\n      const installAdmin = E(agoricNamesAdmin).lookupAdmin('installation');\n      await Promise.all(\n        installationEntries.map(([key, value]) => {\n          produceInstallations[key].resolve(value);\n          return E(installAdmin).update(key, value);\n        }),\n      );\n    }\n\n    // Evaluate the manifest.\n    return runModuleBehaviors({\n      // Remember that `powers` may be arbitrarily broad.\n      allPowers: powers,\n      behaviors: proposalNS,\n      manifest: customManifest || manifest,\n      makeConfig: (name, _permit) => {\n        log('coreProposal:', name);\n        return { options };\n      },\n    });\n  };\n\n  return coreProposalBehavior;\n})({ manifestBundleRef, getManifestCall, customManifest, E });\nbehavior;\n"}]}
2024-02-07T15:44:42.132Z SwingSet: vat: v1: evaluateBundleCap { manifestBundleRef: { bundleID: 'b1-aaefe6af1d3f97e01c9e92129a855db38f17736c1050b87a80b0606f44adca8dc92be334f5b18b4af6f97effb673b85e3744fa9b0851b3f73015d0351e6a5c60' }, manifestGetterName: 'getManifestForVaultFactory', vatAdminSvc: Promise [Promise] {} }
2024-02-07T15:44:43.711Z SwingSet: vat: v1: execute { manifestGetterName: 'getManifestForVaultFactory', bundleExports: [ 'getManifestForVaultFactory' ] }
2024-02-07T15:44:43.752Z block-manager: block 241 commit
2024-02-07T15:44:47.211Z block-manager: block 242 begin
2024-02-07T15:44:47.298Z SwingSet: vat: v1: CORE_EVAL failed: (Error#1)
2024-02-07T15:44:47.298Z SwingSet: vat: v1: Error#1: startVaultFactory not in getManifestForVaultFactory
2024-02-07T15:44:47.298Z SwingSet: vat: v1: Error: startVaultFactory not in getManifestForVaultFactory
at construct ()
at Error (/bundled-source/.../node_modules/@endo/lockdown/node_modules/ses/src/error/tame-error-constructor.js:56)
at makeError (/bundled-source/.../node_modules/@endo/lockdown/node_modules/ses/src/error/assert.js:273)
at fail (/bundled-source/.../node_modules/@endo/lockdown/node_modules/ses/src/error/assert.js:387)
at baseAssert (/bundled-source/.../node_modules/@endo/lockdown/node_modules/ses/src/error/assert.js:407)
at (.../vats/src/core/utils.js:184)
at ()

2024-02-07T15:44:47.299Z SwingSet: xsnap: v1: Error#1 ERROR_NOTE: Sent as error:liveSlots:v1#70001
2024-02-07T15:44:47.299Z SwingSet: ls: v1: Logging sent error stack (Error#1)
2024-02-07T15:44:47.302Z SwingSet: xsnap: v10: UnhandledPromiseRejectionWarning: (RemoteError(error:liveSlots:v1#70001)#1)
2024-02-07T15:44:47.302Z SwingSet: xsnap: v10: RemoteError(error:liveSlots:v1#70001)#1: startVaultFactory not in getManifestForVaultFactory
2024-02-07T15:44:47.302Z SwingSet: xsnap: v10: Error: startVaultFactory not in getManifestForVaultFactory
at construct ()
at Error (/bundled-source/.../node_modules/@endo/lockdown/node_modules/ses/src/error/tame-error-constructor.js:56)
at makeError (/bundled-source/.../node_modules/@endo/lockdown/node_modules/ses/src/error/assert.js:273)
at decodeErrorCommon (/bundled-source/.../node_modules/@endo/marshal/src/marshal.js:281)
at decodeFromSmallcaps (/bundled-source/.../node_modules/@endo/marshal/src/encodeToSmallcaps.js:437)
at fromCapData (/bundled-source/.../node_modules/@endo/marshal/src/marshal.js:356)
at notifyOnePromise (/bundled-source/.../packages/swingset-liveslots/src/liveslots.js:1194)
at notify (/bundled-source/.../packages/swingset-liveslots/src/liveslots.js:1218)
at dispatchToUserspace (/bundled-source/.../packages/swingset-liveslots/src/liveslots.js:1487)
at runWithoutMetering (/bundled-source/.../packages/swingset-xsnap-supervisor/lib/supervisor-subprocess-xsnap.js:60)
at ()

Expected behavior

Proposal is executed and a new instance of vaultFactory is deployed.

Platform Environment

  • what OS are you using? what version of Node.js? : macOS , v16.20.2
  • is there anything special/unusual about your platform? : No
  • what version of the Agoric-SDK are you using? : @agoric/[email protected]

Error: timerBrand: symbol "[Symbol(replaced:35)]"

Describe the bug

When executing the function runAuction in the bootstrap test-liquidation-visibility.js, the liveAuctionSchedule object will have as timerBrand the value Symbol(replaced:35).

Commit 50b2103

The object is returned by the getSchedules method:

const { liveAuctionSchedule } = await EV(
    auctioneerKit.publicFacet,
  ).getSchedules();

liveAuctionSchedule object

LOG: liveAuctionSchedule  {
  clockStep: { relValue: 180n, timerBrand: Symbol(replaced:35) },
  endRate: 6500n,
  endTime: { absValue: 5042n, timerBrand: Symbol(replaced:35) },
  lockTime: { absValue: 1802n, timerBrand: Symbol(replaced:35) },
  startDelay: { relValue: 2n, timerBrand: Symbol(replaced:35) },
  startTime: { absValue: 3602n, timerBrand: Symbol(replaced:35) },
  steps: 8n
}

Error message

  Error {
    message: 'timestamp: timerBrand: symbol "[Symbol(replaced:35)]" - Must be a remotable (TimerBrand)',
  }

Test Hierarchy

Context

We're trying to improve the visibility of liquidation auctions in terms of transparency and ease of analyzing what happened in each auction round. The constraints of this work mostly drawn by @rowgraus and analyzed by us here. As a result we ended up with the conclusion that there's some work that needs to go into inter-protocol source code hence we've forked this repo.

Questions

Questions to Agoric OpCo about what hierarchy should we build our tests and related tools;

  • Is it okay if we have our own directory like liquidationVisibility?
    • If not, which test file we should put our tests?
  • Some of the test tools (like setupServices) are useful for most of the tests but are not exported. We ended up copy/paste them into our tools.js. Is this okay to keep building on?

cc @Chris-Hibbert

Must rebase to `tmp-upgrade-15`

Problem

The current version of agoric-sdk running on mainnet and latest a3p image contain the below piece of code;

https://github.com/Agoric/agoric-sdk/blob/81d2080a8b379e27615b9fa3c5c8de6c8d4ca817/packages/zoe/src/contractFacet/zcfZygote.js#L421

Which checks if the contract subject to upgrade has a prepare method whereas the version of vaultFactory we checked out deprecates prepare and uses start and meta for upgradability. We need to rebase our current work tmp-upgrade-15 and retry.

cc @Jorge-Lopes

Error: Minting would hit total debt limit (bootstrap test)

Describe the bug

At the current stage of the bootstrap test (commit: 67082c1) the test visibility-after-upgrade is failing.

 Error {
    message: 'Minting {"brand":"[Alleged: IST brand]","value":"[105525000n]"} past {"brand":"[Alleged: IST brand]","value":"[0n]"} would hit total debt limit {"brand":"[Alleged: IST brand]","value":"[0n]"}',
  }

To Reproduce

cd packages/vats
yarn test test/bootstrapTests/test-liquidation-visibility.js

Backlog tasks

Backlog:

  • Define hierarchy for vstorage
  • Source code update
    • Vault
      • create or update a recorder to register the
        • vaultData collateralAmount
        • vaultData debtAmount
      • write to an independent child node in vstorage
    • Post Auction
      • create or update a recorder to register the
        • plan collateralForReserve
        • plan shortfallToReserve
        • plan transfersToVault
          • In a first iteration we could calculate the sum of
            • Collateral
            • Debt
      • write to an independent child node in vstorage
  • validate if exposed data according to expected
  • build necessary queries to vstorage to cover the entire liquidation story

Tasks before opening the original PR

  • Open a PR to develop branch
  • Make sure CI passes
  • Fix the git history
  • Write down a PR description with all the considerations and design decisions we've made in it
  • Update a3p assets since the source code is updated

`vstorage` resource analysis

What is the Problem Being Solved?

We need to think on what's the best way store required data on vstorage in the most efficient way

Description of the Design

TBD

Security Considerations

Make sure we're not consuming unnecessary space and not make it harder to update/write new data

Scaling Considerations

Test Plan

Upgrade Considerations

`t.like()` selector must be a non-empty object

Describe the bug

ToDo...

Note: when printed, the expectedPreAuction shows the expected value
Ava docs

checkVisibility()

  const visibilityPath = `published.vaultFactory.managers.manager${managerIndex}.liquidations.${nominalStart.toString()}`;
  const preAuction = readLatest(`${visibilityPath}.vaults.preAuction`);
  const postAuction = readLatest(`${visibilityPath}.vaults.postAuction`);
  const auctionResult = readLatest(`${visibilityPath}.auctionResult`);

  const expectedPreAuction = [];
  for (let i = 0; i < setup.vaults.length; i += 1) {
    expectedPreAuction.push([
      `vault${base + i}`,
      {
        collateralAmount: { value: scale6(setup.vaults[i].atom) },
        debtAmount: { value: scale6(setup.vaults[i].debt) },
      },
    ]);
  }
  t.like(preAuction, expectedPreAuction);

Log messages:

visibility-before-upgrade
  `t.like()` selector must be a non-empty object

  Called with:

  [
    [
      'vault2',
      {
        Collateral: Object { … },
      },
    ],
    [
      'vault1',
      {
        Collateral: Object { … },
      },
    ],
    [
      'vault0',
      {
        Collateral: Object { … },
      },
    ],
  ]

  › checkVisibility (packages/vats/test/bootstrapTests/test-liquidation-visibility.js:295:5)
  › async packages/vats/test/bootstrapTests/test-liquidation-visibility.js:330:3

  ─

`vaults.preAuction` is in reverse order

Describe the bug

Vaults are displayed in reverse order at vaults.preAuction node.

To Reproduce

Steps to reproduce the behavior:

cd agoric-sdk/packages/boot
yarn test test/bootstrapTests/test-liquidation-visilibility.ts -m visibility-before-upgrade

Expected behavior

Vaults should be displayed in the order they are created.

Test Upgradability

What is the Problem Being Solved?

We should make sure liquidation visibility features survive an upgrade.

Contracts update

There are currently 2 FIXME items to address

FIXME 1:

  • after including the liquidationsStorageNode as an argument for makeVaultManagerKitInternal an error is triggered by missing that attribute
  • this change was implemented on commit b4c8a21
  • vaultDirector

FIXME 2:

  • the type of auctionEndTime {TimestampRecord | undefined} needs to match the type of endTime {Timestamp}
  • this change was implemented on commit 21bb9ca
  • vaultManager

`phase` is always `liquidating` at `vaults.postAuction`

Describe the bug

For the vaults that are liquidated the expected phase is liquidated but they are displayed as liquidating at vaults.postAuction

To Reproduce

Steps to reproduce the behavior:

cd agoric-sdk/packages/boot
yarn test test/bootstrapTests/test-liquidation-visilibility.ts -m visibility-before-upgrade

Expected behavior

phase should show the correct value.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.