@polkadot/api#Keyring TypeScript Examples

The following examples show how to use @polkadot/api#Keyring. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From bodhi.js with Apache License 2.0 7 votes vote down vote up
setup = async (urlOverwrite?: string) => {
  const url = urlOverwrite || process.env.ENDPOINT_URL || DEFAULT_URL;
  const seed = process.env.SEED;

  const provider = new Provider({
    provider: new WsProvider(url)
  });

  await provider.api.isReady;

  console.log(`provider connected to ${url}`);

  let pair: KeyringPair;
  if (seed) {
    const keyring = new Keyring({ type: 'sr25519' });
    pair = keyring.addFromUri(seed);
  } else {
    const testPairs = createTestPairs();
    pair = testPairs.alice;
  }

  const signingKey = new AccountSigningKey(provider.api.registry);
  signingKey.addKeyringPair(pair);

  const wallet = new Signer(provider, pair.address, signingKey);
  return {
    wallet,
    provider,
    pair
  };
}
Example #2
Source File: spec.ts    From polkadot-launch with MIT License 6 votes vote down vote up
// Add additional authorities to chain spec in `session.keys`
export async function addAuthority(spec: string, name: string) {
	await cryptoWaitReady();

	const sr_keyring = new Keyring({ type: "sr25519" });
	const sr_account = sr_keyring.createFromUri(`//${nameCase(name)}`);
	const sr_stash = sr_keyring.createFromUri(`//${nameCase(name)}//stash`);

	const ed_keyring = new Keyring({ type: "ed25519" });
	const ed_account = ed_keyring.createFromUri(`//${nameCase(name)}`);

	const ec_keyring = new Keyring({ type: "ecdsa" });
	const ec_account = ec_keyring.createFromUri(`//${nameCase(name)}`);

	let key = [
		sr_stash.address,
		sr_stash.address,
		{
			grandpa: ed_account.address,
			babe: sr_account.address,
			im_online: sr_account.address,
			parachain_validator: sr_account.address,
			authority_discovery: sr_account.address,
			para_validator: sr_account.address,
			para_assignment: sr_account.address,
			beefy: encodeAddress(ec_account.publicKey),
		},
	];

	let rawdata = fs.readFileSync(spec);
	let chainSpec = JSON.parse(rawdata);

	let keys = getAuthorityKeys(chainSpec);
	keys.push(key);

	let data = JSON.stringify(chainSpec, null, 2);
	fs.writeFileSync(spec, data);
	console.log(`  ? Added Genesis Authority ${name}`);
}
Example #3
Source File: index.ts    From parachain-launch with Apache License 2.0 6 votes vote down vote up
getAddress = (val: string) => {
  try {
    const addr = decodeAddress(val);
    return encodeAddress(addr);
  } catch {}

  const keyring = new Keyring();
  const pair = keyring.createFromUri(`//${_.startCase(val)}`, undefined, 'sr25519');

  return pair.address;
}
Example #4
Source File: system.test.ts    From interbtc-api with Apache License 2.0 6 votes vote down vote up
describe("systemAPI", () => {
    let api: ApiPromise;
    let sudoAccount: KeyringPair;
    let interBtcAPI: InterBtcApi;
    let keyring: Keyring;

    before(async () => {
        api = await createSubstrateAPI(PARACHAIN_ENDPOINT);
        keyring = new Keyring({ type: "sr25519" });
        sudoAccount = keyring.addFromUri(SUDO_URI);
        interBtcAPI = new DefaultInterBtcApi(api, "regtest", sudoAccount, ESPLORA_BASE_PATH);
    });

    after(async () => {
        api.disconnect();
    });

    it("should getCurrentBlockNumber", async () => {
        const currentBlockNumber = await interBtcAPI.system.getCurrentBlockNumber();
        assert.isDefined(currentBlockNumber);
    });

    it("should getStatusCode", async () => {
        const statusCode = await interBtcAPI.system.getStatusCode();
        assert.isDefined(statusCode);
    });

    // TODO: Unskip once differences between rococo-local and standalone are fixed
    it.skip("should setCode", async () => {
        const code = fs.readFileSync(
            path.join(__dirname, "../../../mock/rococo_runtime.compact.wasm")
        ).toString("hex");
        await sudo(interBtcAPI, () => interBtcAPI.system.setCode(code));
    });
});
Example #5
Source File: test-sudo.ts    From moonbeam with GNU General Public License v3.0 6 votes vote down vote up
describeDevMoonbeam("Sudo - Only sudo account - test gas", (context) => {
  let alith: KeyringPair;
  before("Setup genesis account for substrate", async () => {
    const keyring = new Keyring({ type: "ethereum" });
    alith = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
  });
  it("should NOT be able to call sudo with another account than sudo account", async function () {
    await createBlockWithExtrinsic(
      context,
      alith,
      context.polkadotApi.tx.sudo.sudo(
        context.polkadotApi.tx.parachainStaking.setParachainBondAccount(GENESIS_ACCOUNT)
      )
    );

    await verifyLatestBlockFees(context, expect);
  });
});
Example #6
Source File: transferTokens.ts    From community-repo with GNU General Public License v3.0 6 votes vote down vote up
async getFaucetAccount(): Promise<NamedKeyringPair | null> {
    const jsonBackupFilePath = `../../accounts/${process.env.ACCOUNT_JSON_NAME}`;
    let accountJsonObj;

    try {
      accountJsonObj = require(jsonBackupFilePath);
    } catch (e) {
      console.error(
        'Provided backup file is not valid or cannot be accessed'
      );

      throw new Error('Server error [1]');
    }

    const keyring = new Keyring();
    let account: NamedKeyringPair;
    try {
      // Try adding and retrieving the keys in order to validate that the backup file is correct
      keyring.addFromJson(accountJsonObj);
      account = keyring.getPair(accountJsonObj.address) as NamedKeyringPair; // We can be sure it's named, because we forced it before
    } catch (e) {
      console.error('Provided backup file is not valid');
      throw new Error('Server error [2]');
    }

    return account;
  }
Example #7
Source File: test-balance-transfer-txwrapper-substrate.ts    From moonbeam with GNU General Public License v3.0 5 votes vote down vote up
describeDevMoonbeam("Balance transfer - txwrapper", (context) => {
  before("Create block with transfer to test account of 512", async function () {
    // txwrapper takes more time to initiate :/
    this.timeout(10000);

    const [
      { block },
      blockHash,
      genesisHash,
      metadataRpc,
      { specVersion, transactionVersion, specName },
    ] = await Promise.all([
      rpcToLocalNode(context.rpcPort, "chain_getBlock"),
      rpcToLocalNode(context.rpcPort, "chain_getBlockHash"),
      rpcToLocalNode(context.rpcPort, "chain_getBlockHash", [0]),
      rpcToLocalNode(context.rpcPort, "state_getMetadata"),
      rpcToLocalNode(context.rpcPort, "state_getRuntimeVersion"),
    ]);

    const registry = getRegistry({
      chainName: "Moonriver",
      specName,
      specVersion,
      metadataRpc,
    });
    const unsigned = substrateMethods.balances.transfer(
      {
        dest: TEST_ACCOUNT,
        value: 512,
      },
      {
        address: GENESIS_ACCOUNT,
        blockHash,
        blockNumber: registry.createType("BlockNumber", block.header.number).toNumber(),
        eraPeriod: 64,
        genesisHash,
        metadataRpc,
        nonce: 0, // Assuming this is Gerald's first tx on the chain
        specVersion,
        tip: 0,
        transactionVersion,
      },
      {
        metadataRpc,
        registry,
      }
    );

    const signingPayload = createSigningPayload(unsigned, { registry });
    const keyring = new Keyring({ type: "ethereum" });
    const genesis = await keyring.addFromUri(GENESIS_ACCOUNT_PRIVATE_KEY, null, "ethereum");
    const signature = signWith(genesis, signingPayload, {
      metadataRpc,
      registry,
    });
    // Serialize a signed transaction.
    const tx = createSignedTx(unsigned, signature, { metadataRpc, registry });

    await rpcToLocalNode(context.rpcPort, "author_submitExtrinsic", [tx]);
    await context.createBlock();
  });

  it("should increase to account", async function () {
    expect(await context.web3.eth.getBalance(TEST_ACCOUNT, 0)).to.equal("0");
    expect(await context.web3.eth.getBalance(TEST_ACCOUNT, 1)).to.equal("512");
  });

  it("should reflect balance identically on polkadot/web3", async function () {
    const block1Hash = await context.polkadotApi.rpc.chain.getBlockHash(1);
    expect(await context.web3.eth.getBalance(GENESIS_ACCOUNT, 1)).to.equal(
      (
        (await context.polkadotApi.query.system.account.at(block1Hash, GENESIS_ACCOUNT)) as any
      ).data.free.toString()
    );
  });
  it("should check fees", async function () {
    await verifyLatestBlockFees(context, expect, BigInt(512));
  });
});
Example #8
Source File: Keyring.ts    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
static fromKeyPair(pair: Keypair, name?: string): KeyringPair {
    const keyring = new Keyring({ type: 'sr25519' });
    return GearKeyring.unlock(keyring.addFromPair(pair, { name }));
  }
Example #9
Source File: test-runtime-upgrade.ts    From moonbeam with GNU General Public License v3.0 5 votes vote down vote up
describeParachain(
  `Runtime upgrade ${RUNTIME_VERSION}`,
  {
    parachain: {
      chain: "moonbase-local",
      runtime: `runtime-${baseRuntime}`,
      binary: "local",
    },
    relaychain: {
      binary: "local",
    },
  },
  (context) => {
    if (localVersion !== alreadyReleased) {
      it("should not fail", async function () {
        // Expected to take 10 blocks for upgrade + 4 blocks to check =>
        // ~200000 + init 60000 + error marging 140000
        this.timeout(400000);
        const keyring = new Keyring({ type: "ethereum" });
        const alith = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");

        const currentVersion = await (
          (await context.polkadotApiParaone.query.system.lastRuntimeUpgrade()) as any
        ).unwrap();
        expect(currentVersion.toJSON()).to.deep.equal({
          specVersion: Number(baseRuntime),
          specName: "moonbase",
        });
        console.log(
          `Current runtime: ✅ runtime ${currentVersion.specName.toString()} ` +
            `${currentVersion.specVersion.toString()}`
        );

        await context.upgradeRuntime(alith, "moonbase", RUNTIME_VERSION);

        process.stdout.write(`Checking on-chain runtime version ${localVersion}...`);
        expect(
          await (await context.polkadotApiParaone.query.system.lastRuntimeUpgrade()).toJSON()
        ).to.deep.equal({
          specVersion: Number(localVersion),
          specName: "moonbase",
        });
        process.stdout.write("✅\n");

        process.stdout.write("Waiting extra block being produced...");
        await context.waitBlocks(4); // Make sure the new runtime is producing blocks
        process.stdout.write(`✅ total ${context.blockNumber} block produced\n`);
      });
    }
  }
);
Example #10
Source File: index.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
keyring = new Keyring()
Example #11
Source File: Keyring.ts    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
static async fromSuri(suri: string, name?: string): Promise<KeyringPair> {
    const keyring = new Keyring({ type: 'sr25519' });
    await waitReady();
    const keyPair = keyring.addFromUri(suri, { name });
    return keyPair;
  }
Example #12
Source File: test-crowdloan.ts    From moonbeam with GNU General Public License v3.0 5 votes vote down vote up
describeDevMoonbeam("Crowdloan", (context) => {
  let sudoAccount: KeyringPair;

  let numberOfAccounts: number = 1000; // min 2
  let largInput: [string, string, bigint][];

  before("Setup genesis account for substrate", async () => {
    // We shouldnt be able to register as many accounts unless we do it in batches
    numberOfAccounts = Number(
      (await context.polkadotApi.consts.crowdloanRewards.maxInitContributors) as any
    );
    const keyring = new Keyring({ type: "ethereum" });
    sudoAccount = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
  });

  it("should be able to register many accounts - batch : " + numberOfAccounts, async function () {
    // should create a bunch of test eth accounts
    this.timeout(20000);
    let web3 = new Web3();
    let accounts = new Array(numberOfAccounts).fill(0).map((_, i) => web3.eth.accounts.create());
    largInput = accounts.map((acc: Account, i: number) => {
      return [
        acc.address + "111111111111111111111111",
        acc.address,
        (3_000_000n * GLMR) / BigInt(numberOfAccounts),
      ];
    });
    expect(largInput.length).to.eq(numberOfAccounts);
    expect(largInput[0][1] !== largInput[numberOfAccounts - 1][1]).to.eq(true);

    // should be able to register many accounts
    await context.polkadotApi.tx.utility
      .batch([
        await context.polkadotApi.tx.sudo.sudo(
          context.polkadotApi.tx.crowdloanRewards.initializeRewardVec(
            largInput.slice(0, Math.floor(numberOfAccounts / 3))
          )
        ),
        await context.polkadotApi.tx.sudo.sudo(
          context.polkadotApi.tx.crowdloanRewards.initializeRewardVec(
            largInput.slice(
              Math.floor(numberOfAccounts / 3),
              Math.floor((numberOfAccounts * 2) / 3)
            )
          )
        ),
        await context.polkadotApi.tx.sudo.sudo(
          context.polkadotApi.tx.crowdloanRewards.initializeRewardVec(
            largInput.slice(Math.floor((numberOfAccounts * 2) / 3), numberOfAccounts)
          )
        ),
      ])
      .signAndSend(sudoAccount);
    await context.createBlock();

    let initBlock = (await context.polkadotApi.query.crowdloanRewards.initRelayBlock()) as any;

    // Complete initialization
    await context.polkadotApi.tx.sudo
      .sudo(
        context.polkadotApi.tx.crowdloanRewards.completeInitialization(
          initBlock.toBigInt() + VESTING_PERIOD
        )
      )
      .signAndSend(sudoAccount);
    await context.createBlock();

    await Promise.all(
      largInput.map(async (input) => {
        expect((await getAccountPayable(context, input[1])).totalReward.toBigInt()).to.equal(
          (3_000_000n * GLMR) / BigInt(numberOfAccounts)
        );
      })
    );
  });
});
Example #13
Source File: Keyring.ts    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
static fromJson(keypairJson: KeyringPair$Json | string, passphrase?: string): KeyringPair {
    const json: KeyringPair$Json = isString(keypairJson) ? JSON.parse(keypairJson) : keypairJson;
    const keyring = new Keyring().addFromJson(json);
    return GearKeyring.unlock(keyring, passphrase);
  }
Example #14
Source File: shared.ts    From commonwealth with GNU General Public License v3.0 5 votes vote down vote up
public keyring(useEd25519 = false) {
    return new Keyring({
      type: useEd25519 ? 'ed25519' : 'sr25519',
      ss58Format: this._ss58Format,
    });
  }
Example #15
Source File: hrmp-setup.ts    From interbtc-api with Apache License 2.0 5 votes vote down vote up
async function main(): Promise<void> {
    await cryptoWaitReady();
    const keyring = new Keyring({ type: "sr25519" });
    const userKeyring = keyring.addFromUri(ACCOUNT_URI);
    const api = await createSubstrateAPI(PARACHAIN_ENDPOINT);

    const parent = api.createType<XcmV1MultiLocation>("XcmV1MultiLocation", {
        parents: 1,
        interior: api.createType("XcmV1MultilocationJunctions", {
            here: true
        })
    });
    const setupTx = api.tx.sudo.sudo(api.tx.polkadotXcm.forceXcmVersion(parent, 2));

    // transactions used for the purestake alpha test
    const westendRequestXcmTx = construct_xcm(api, 1002, "0x3300e80300000800000000040000");
    const westendCancelXcmTx = construct_xcm(api, 1002, "0x3306ea030000e9030000");
    const westendAcceptXcmTx = construct_xcm(api, 1002, "0x3301e8030000");
    
    // transactions used for rococo-local test
    const rococoRequestXcmTx = construct_xcm(api, 2000, "0x1700b80b00000800000000900100");
    const rococoAcceptXcmTx = construct_xcm(api, 2000, "0x1701d0070000");

    // transactions to be used on kintsugi
    const kinstugiRequestXcmTx = construct_xcm(api, 2092, "0x3c00d0070000e803000000900100");

    // note: very important to use `.method`, otherwise it includes signing info. The polkadot.js
    // app strips the signing info when you try to decode it, but if you use the call data in
    // an extrinsic (e.g. in democracy), it will fail.
    console.log("Call data: " + rococoRequestXcmTx.method.toHex());
    console.log("Call hash: " + rococoRequestXcmTx.method.hash.toHex());

    const transactionAPI = new DefaultTransactionAPI(api, userKeyring);

    console.log("Constructed the tx, broadcasting first...");
    await transactionAPI.sendLogged(setupTx, undefined);
    console.log("broadcasting second...");
    await transactionAPI.sendLogged(api.tx.sudo.sudo(rococoAcceptXcmTx), undefined);

    api.disconnect();
}
Example #16
Source File: rpc.ts    From polkadot-launch with MIT License 5 votes vote down vote up
// Submit an extrinsic to the relay chain to register a parachain.
// Uses the Alice account which is known to be Sudo for the relay chain.
export async function registerParachain(
	api: ApiPromise,
	id: string,
	wasm: string,
	header: string,
	finalization: boolean = false
) {
	return new Promise<void>(async (resolvePromise, reject) => {
		await cryptoWaitReady();

		const keyring = new Keyring({ type: "sr25519" });
		const alice = keyring.addFromUri("//Alice");

		let paraGenesisArgs = {
			genesis_head: header,
			validation_code: wasm,
			parachain: true,
		};
		let genesis = api.createType("ParaGenesisArgs", paraGenesisArgs);

		const nonce = Number((await api.query.system.account(alice.address)).nonce);

		console.log(
			`--- Submitting extrinsic to register parachain ${id}. (nonce: ${nonce}) ---`
		);
		const unsub = await api.tx.sudo
			.sudo(api.tx.parasSudoWrapper.sudoScheduleParaInitialize(id, genesis))
			.signAndSend(alice, { nonce: nonce, era: 0 }, (result) => {
				console.log(`Current status is ${result.status}`);
				if (result.status.isInBlock) {
					console.log(
						`Transaction included at blockHash ${result.status.asInBlock}`
					);
					if (finalization) {
						console.log("Waiting for finalization...");
					} else {
						unsub();
						resolvePromise();
					}
				} else if (result.status.isFinalized) {
					console.log(
						`Transaction finalized at blockHash ${result.status.asFinalized}`
					);
					unsub();
					resolvePromise();
				} else if (result.isError) {
					console.log(`Transaction Error`);
					reject(`Transaction Error`);
				}
			});
	});
}
Example #17
Source File: test-crowdloan.ts    From moonbeam with GNU General Public License v3.0 5 votes vote down vote up
describeDevMoonbeam("Crowdloan", (context) => {
  let genesisAccount: KeyringPair, sudoAccount: KeyringPair;

  before("Setup genesis account for substrate", async () => {
    const keyring = new Keyring({ type: "ethereum" });
    genesisAccount = await keyring.addFromUri(GENESIS_ACCOUNT_PRIVATE_KEY, null, "ethereum");
    sudoAccount = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
  });

  it("should check initial state", async function () {
    // check that genesis has genesis balance
    expect(Number(await context.web3.eth.getBalance(GENESIS_ACCOUNT))).to.eq(
      Number(GENESIS_ACCOUNT_BALANCE)
    );
    // check that genesis is not registered
    const isPayable = await getAccountPayable(context, GENESIS_ACCOUNT);
    expect(isPayable).to.equal(null);
  });

  it("should be able to register the genesis account for reward", async function () {
    // should be able to register the genesis account for reward
    await context.polkadotApi.tx.sudo
      .sudo(
        context.polkadotApi.tx.crowdloanRewards.initializeRewardVec([
          [relayChainAddress, GENESIS_ACCOUNT, 3_000_000n * GLMR],
        ])
      )
      .signAndSend(sudoAccount);
    await context.createBlock();

    await verifyLatestBlockFees(context, expect, 3_000_000n);

    let initBlock = (await context.polkadotApi.query.crowdloanRewards.initRelayBlock()) as any;

    // Complete initialization
    await context.polkadotApi.tx.sudo
      .sudo(
        context.polkadotApi.tx.crowdloanRewards.completeInitialization(
          initBlock.toBigInt() + VESTING_PERIOD
        )
      )
      .signAndSend(sudoAccount);
    await context.createBlock();

    expect((await getAccountPayable(context, GENESIS_ACCOUNT)).totalReward.toBigInt()).to.equal(
      3_000_000n * GLMR
    );
    let isInitialized = await context.polkadotApi.query.crowdloanRewards.initialized();
    expect(isInitialized.toHuman()).to.be.true;
  });
});
Example #18
Source File: initialize.test.ts    From interbtc-api with Apache License 2.0 4 votes vote down vote up
describe("Initialize parachain state", () => {
    let api: ApiPromise;
    let userInterBtcAPI: InterBtcApi;
    let sudoInterBtcAPI: InterBtcApi;
    let bitcoinCoreClient: BitcoinCoreClient;
    let keyring: Keyring;

    let sudoAccount: KeyringPair;
    let userAccount: KeyringPair;

    let vault_1: KeyringPair;
    let vault_2: KeyringPair;
    let vault_3: KeyringPair;
    let vault_to_ban: KeyringPair;
    let vault_to_liquidate: KeyringPair;

    let wrappedCurrency: WrappedCurrency;
    let collateralCurrency: CollateralCurrency;

    function accountIdFromKeyring(keyPair: KeyringPair): AccountId {
        return newAccountId(api, keyPair.address);
    }

    async function waitForRegister(api: VaultsAPI, accountId: AccountId, collateralCurrency: CurrencyIdLiteral) {
        while (true) {
            try {
                await api.get(accountId, collateralCurrency);
                return;
            } catch (e) { console.log(e); }
            await sleep(SLEEP_TIME_MS);
        }
    }

    before(async function () {
        api = await createSubstrateAPI(PARACHAIN_ENDPOINT);
        keyring = new Keyring({ type: "sr25519" });
        sudoAccount = keyring.addFromUri(SUDO_URI);
        userAccount = keyring.addFromUri(USER_1_URI);
        vault_1 = keyring.addFromUri(VAULT_1_URI);
        vault_2 = keyring.addFromUri(VAULT_2_URI);
        vault_3 = keyring.addFromUri(VAULT_3_URI);
        vault_to_ban = keyring.addFromUri(VAULT_TO_BAN_URI);
        vault_to_liquidate = keyring.addFromUri(VAULT_TO_LIQUIDATE_URI);

        bitcoinCoreClient = new BitcoinCoreClient(
            BITCOIN_CORE_NETWORK,
            BITCOIN_CORE_HOST,
            BITCOIN_CORE_USERNAME,
            BITCOIN_CORE_PASSWORD,
            BITCOIN_CORE_PORT,
            BITCOIN_CORE_WALLET
        );

        userInterBtcAPI = new DefaultInterBtcApi(api, "regtest", userAccount, ESPLORA_BASE_PATH);
        sudoInterBtcAPI = new DefaultInterBtcApi(api, "regtest", sudoAccount, ESPLORA_BASE_PATH);

        wrappedCurrency = userInterBtcAPI.getWrappedCurrency();
        collateralCurrency = getCorrespondingCollateralCurrency(userInterBtcAPI.getGovernanceCurrency());
        const collateralCurrencyLiteral = tickerToCurrencyIdLiteral(collateralCurrency.ticker);
        const vaultCollateralPairs: [KeyringPair, CurrencyIdLiteral][] = [
            [vault_1, collateralCurrencyLiteral],
            [vault_2, collateralCurrencyLiteral],
            [vault_3, collateralCurrencyLiteral],
            [vault_to_ban, collateralCurrencyLiteral],
            [vault_to_liquidate, collateralCurrencyLiteral]
        ];
        // wait for all vaults to register
        await Promise.all(
            vaultCollateralPairs
                .map(([keyring, collateral]): [AccountId, CurrencyIdLiteral] => [accountIdFromKeyring(keyring), collateral])
                .map(([accountId, collateral]) => waitForRegister(userInterBtcAPI.vaults, accountId, collateral))
        );
    });

    after(async () => {
        api.disconnect();
    });

    it("should set the stable confirmations and ready the BTC-Relay", async () => {
        // Speed up the process by only requiring 0 parachain and 0 bitcoin confirmations
        const stableBitcoinConfirmationsToSet = 0;
        const stableParachainConfirmationsToSet = 0;
        let [stableBitcoinConfirmations, stableParachainConfirmations] = await Promise.all([
            userInterBtcAPI.btcRelay.getStableBitcoinConfirmations(),
            userInterBtcAPI.btcRelay.getStableParachainConfirmations()
        ]);

        if (stableBitcoinConfirmations != 0 || stableParachainConfirmations != 0) {
            await initializeStableConfirmations(
                api,
                {
                    bitcoinConfirmations: stableBitcoinConfirmationsToSet,
                    parachainConfirmations: stableParachainConfirmationsToSet
                },
                sudoAccount,
                bitcoinCoreClient
            );
            [stableBitcoinConfirmations, stableParachainConfirmations] = await Promise.all([
                userInterBtcAPI.btcRelay.getStableBitcoinConfirmations(),
                userInterBtcAPI.btcRelay.getStableParachainConfirmations()
            ]);
        }
        assert.equal(stableBitcoinConfirmationsToSet, stableBitcoinConfirmations, "Setting the Bitcoin confirmations failed");
        assert.equal(stableParachainConfirmationsToSet, stableParachainConfirmations, "Setting the Parachain confirmations failed");
    });

    it("should set the exchange rate", async () => {
        async function setCollateralExchangeRate<C extends CollateralUnit>(value: Big, currency: Currency<C>) {
            const exchangeRate = new ExchangeRate<Bitcoin, BitcoinUnit, typeof currency, typeof currency.units>(Bitcoin, currency, value);
            // result will be medianized
            await initializeExchangeRate(exchangeRate, sudoInterBtcAPI.oracle);
        }
        const exchangeRateValue = new Big("3855.23187");
        await setCollateralExchangeRate(exchangeRateValue, Polkadot);
        await setCollateralExchangeRate(exchangeRateValue, Kusama);
    });

    it("should set BTC tx fees", async () => {
        const setFeeEstimate = new Big(1);
        let getFeeEstimate = await sudoInterBtcAPI.oracle.getBitcoinFees();
        if (!getFeeEstimate) {
            await initializeBtcTxFees(setFeeEstimate, sudoInterBtcAPI.oracle);
            // just check that this is set since we medianize results
            getFeeEstimate = await sudoInterBtcAPI.oracle.getBitcoinFees();
        }
        assert.isDefined(getFeeEstimate);
    });

    it("should enable vault nomination", async () => {
        let isNominationEnabled = await sudoInterBtcAPI.nomination.isNominationEnabled();
        if (!isNominationEnabled) {
            await initializeVaultNomination(true, sudoInterBtcAPI.nomination);
            isNominationEnabled = await sudoInterBtcAPI.nomination.isNominationEnabled();
        }
        assert.isTrue(isNominationEnabled);
    });

    it("should issue 0.00007 wrapped", async () => {
        const wrappedToIssue = newMonetaryAmount(0.00007, wrappedCurrency, true);
        const feesToPay = await userInterBtcAPI.issue.getFeesToPay(wrappedToIssue);
        const userAccountId = newAccountId(api, userAccount.address);
        const userWrappedBefore = (await userInterBtcAPI.tokens.balance(wrappedCurrency, userAccountId)).free;

        await initializeIssue(
            userInterBtcAPI, bitcoinCoreClient, userAccount, wrappedToIssue, newVaultId(api, vault_1.address, collateralCurrency, wrappedCurrency)
        );
        const collateralCurrencyLiteral = tickerToCurrencyIdLiteral(collateralCurrency.ticker);
        const [userWrappedAfter, totalIssuance, vaultIssuedAmount] = await Promise.all([
            userInterBtcAPI.tokens.balance(wrappedCurrency, userAccountId),
            userInterBtcAPI.tokens.total(wrappedCurrency),
            userInterBtcAPI.vaults.getIssuedAmount(newAccountId(api, vault_1.address), collateralCurrencyLiteral)
        ]);

        assert.equal(
            userWrappedBefore.add(wrappedToIssue).sub(feesToPay).toString(),
            userWrappedAfter.free.toString(),
            "Issued amount is different from the requested amount"
        );
        // TODO: get the total issuance and vault issued amount before and calculate difference
        // so that this test can be run more than once without resetting the chain
        // assert.equal(totalIssuance.toString(), wrappedToIssue.toString());
        // assert.equal(vaultIssuedAmount.toString(), wrappedToIssue.toString());
    });

    it("should redeem 0.00005 InterBtc", async () => {
        const wrappedToRedeem = newMonetaryAmount(0.00005, wrappedCurrency, true);
        const redeemAddress = "bcrt1qed0qljupsmqhxul67r7358s60reqa2qtte0kay";
        await userInterBtcAPI.redeem.request(wrappedToRedeem, redeemAddress);

        const redeemRequests = await userInterBtcAPI.redeem.list();
        assert.isAtLeast(
            redeemRequests.length,
            1,
            "Error in initialization setup. Should have at least 1 redeem request"
        );
    });
});
Example #19
Source File: test-crowdloan.ts    From moonbeam with GNU General Public License v3.0 4 votes vote down vote up
describeDevMoonbeam("Crowdloan", (context) => {
  let sudoAccount: KeyringPair;

  before("Setup genesis account for substrate", async () => {
    const keyring = new Keyring({ type: "ethereum" });
    sudoAccount = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
  });

  it("should be able to initialize through democracy", async function () {
    let calls = [];
    // We are gonna put the initialization and completion in a batch_all utility call
    calls.push(
      context.polkadotApi.tx.crowdloanRewards.initializeRewardVec([
        [relayChainAddress, GENESIS_ACCOUNT, 1_500_000n * GLMR],
        [relayChainAddress_2, null, 1_500_000n * GLMR],
      ])
    );

    let initBlock = (await context.polkadotApi.query.crowdloanRewards.initRelayBlock()) as any;
    calls.push(
      context.polkadotApi.tx.crowdloanRewards.completeInitialization(
        initBlock.toBigInt() + VESTING_PERIOD
      )
    );

    // Here we build the utility call
    const proposal = context.polkadotApi.tx.utility.batchAll(calls);

    // We encode the proposal
    let encodedProposal = (proposal as SubmittableExtrinsic)?.method.toHex() || "";
    let encodedHash = blake2AsHex(encodedProposal);

    // Submit the pre-image
    await context.polkadotApi.tx.democracy.notePreimage(encodedProposal).signAndSend(sudoAccount);

    await context.createBlock();

    // Propose
    await context.polkadotApi.tx.democracy
      .propose(encodedHash, 1000n * GLMR)
      .signAndSend(sudoAccount);

    await context.createBlock();
    const publicPropCount = await context.polkadotApi.query.democracy.publicPropCount();

    // we only use sudo to enact the proposal
    await context.polkadotApi.tx.sudo
      .sudoUncheckedWeight(
        context.polkadotApi.tx.democracy.enactProposal(encodedHash, publicPropCount),
        1
      )
      .signAndSend(sudoAccount);

    await context.createBlock();

    let isInitialized = await context.polkadotApi.query.crowdloanRewards.initialized();

    expect(isInitialized.toHuman()).to.be.true;

    // Get reward info of associated
    let reward_info_associated = await getAccountPayable(context, GENESIS_ACCOUNT);

    // Get reward info of unassociated
    let reward_info_unassociated = (
      (await context.polkadotApi.query.crowdloanRewards.unassociatedContributions(
        relayChainAddress_2
      )) as any
    ).unwrap();

    // Check payments
    expect(reward_info_associated.totalReward.toBigInt()).to.equal(1_500_000n * GLMR);

    expect(reward_info_associated.claimedReward.toBigInt()).to.equal(450_000n * GLMR);

    expect(reward_info_unassociated.totalReward.toBigInt()).to.equal(1_500_000n * GLMR);

    expect(reward_info_unassociated.claimedReward.toBigInt()).to.equal(0n);

    // check balances
    const account = (await context.polkadotApi.query.system.account(GENESIS_ACCOUNT)) as any;
    expect(account.data.free.toBigInt() - GENESIS_ACCOUNT_BALANCE).to.equal(
      reward_info_associated.claimedReward.toBigInt()
    );
  });
});
Example #20
Source File: tokens.test.ts    From interbtc-api with Apache License 2.0 4 votes vote down vote up
describe("TokensAPI", () => {
    let api: ApiPromise;
    let user1Account: KeyringPair;
    let user2Account: KeyringPair;
    let interBtcAPI: InterBtcApi;

    before(async () => {
        api = await createSubstrateAPI(PARACHAIN_ENDPOINT);
        const keyring = new Keyring({ type: "sr25519" });
        user1Account = keyring.addFromUri(USER_1_URI);
        user2Account = keyring.addFromUri(USER_2_URI);
        interBtcAPI = new DefaultInterBtcApi(api, "regtest", user1Account, ESPLORA_BASE_PATH);
    });

    after(() => {
        return api.disconnect();
    });

    it("should subscribe to balance updates", async () => {
        for (const currency of [...CollateralCurrency]) {
            await testBalanceSubscription(currency as Currency<CurrencyUnit>);
        }
    });

    async function testBalanceSubscription<U extends CurrencyUnit>(currency: Currency<U>): Promise<void> {
        // Subscribe and receive two balance updates
        let updatedBalance = new ChainBalance<U>(currency);
        let updatedAccount = "";
        function balanceUpdateCallback(account: string, newBalance:  ChainBalance<U>) {
            updatedBalance = newBalance;
            updatedAccount = account;
        }
        const amountToUpdateUser2sAccountBy = newMonetaryAmount(10, currency, true);
        const user2BalanceBeforeTransfer =
            await interBtcAPI.tokens.balance<typeof currency.units>(currency, newAccountId(api, user2Account.address));
        const unsubscribe = await interBtcAPI.tokens.subscribeToBalance(currency, user2Account.address, balanceUpdateCallback);

        // Send the first transfer, expect the callback to be called with correct values
        await interBtcAPI.tokens.transfer(user2Account.address, amountToUpdateUser2sAccountBy);
        assert.equal(updatedAccount, user2Account.address);
        const expectedUser2BalanceAfterFirstTransfer = new ChainBalance<U>(
            currency,
            user2BalanceBeforeTransfer.free.add(amountToUpdateUser2sAccountBy).toBig(),
            user2BalanceBeforeTransfer.transferable.add(amountToUpdateUser2sAccountBy).toBig(),
            user2BalanceBeforeTransfer.reserved.toBig()
        );
        assert.equal(updatedBalance.toString(), expectedUser2BalanceAfterFirstTransfer.toString());

        // Send the second transfer, expect the callback to be called with correct values
        await interBtcAPI.tokens.transfer(user2Account.address, amountToUpdateUser2sAccountBy);
        assert.equal(updatedAccount, user2Account.address);
        const expectedUser2BalanceAfterSecondTransfer = new ChainBalance<U>(
            currency,
            expectedUser2BalanceAfterFirstTransfer.free.add(amountToUpdateUser2sAccountBy).toBig(),
            expectedUser2BalanceAfterFirstTransfer.transferable.add(amountToUpdateUser2sAccountBy).toBig(),
            expectedUser2BalanceAfterFirstTransfer.reserved.toBig()
        );
        assert.equal(updatedBalance.toString(), expectedUser2BalanceAfterSecondTransfer.toString());

        // TODO: Commented out because it blocks release, fix.
        // Fails because it conflicts with the escrowAPI test:
        // Error: tokens.LiquidityRestrictions Failed because liquidity restrictions due to locking

        // if (currency.name === Interlay.name) {
        //     const currentBlockNumber = await interBtcAPI.system.getCurrentBlockNumber();
        //     const unlockHeightDiff = (await escrowAPI.getSpan()).toNumber();

        //     const amountToFreeze = newMonetaryAmount(600, currency as Currency<U>, true);
        //     await escrowAPI.createLock(
        //         amountToFreeze as unknown as MonetaryAmount<Currency<GovernanceUnit>, GovernanceUnit>,
        //         currentBlockNumber + unlockHeightDiff
        //     );

        //     const expectedUser2BalanceAfterEscrowLock = new ChainBalance<U>(
        //         currency,
        //         expectedUser2BalanceAfterSecondTransfer.free.toBig(),
        //         expectedUser2BalanceAfterSecondTransfer.transferable.sub(amountToFreeze).toBig(),
        //         expectedUser2BalanceAfterSecondTransfer.reserved.toBig()
        //     );

        //     assert.equal(updatedAccount, user2Account.address);
        //     assert.equal(updatedBalance.toString(), expectedUser2BalanceAfterEscrowLock.toString());
        // }
        unsubscribe();
    }
});
Example #21
Source File: test-precompile-democracy.ts    From moonbeam with GNU General Public License v3.0 4 votes vote down vote up
describeDevMoonbeam("Democracy - vote on referendum", (context) => {
  let genesisAccount: KeyringPair, alith: KeyringPair;
  let encodedHash: `0x${string}`;
  let enactmentPeriod, votingPeriod;
  let iFace: Interface;

  before("Setup genesis account for substrate", async () => {
    const keyring = new Keyring({ type: "ethereum" });
    genesisAccount = await keyring.addFromUri(GENESIS_ACCOUNT_PRIVATE_KEY, null, "ethereum");
    alith = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");

    iFace = await deployAndInterfaceContract(context, "Democracy");

    // enactmentPeriod
    enactmentPeriod = await context.polkadotApi.consts.democracy.enactmentPeriod;
    // votingPeriod
    votingPeriod = await context.polkadotApi.consts.democracy.votingPeriod;

    // encodedHash
    encodedHash = await notePreimagePrecompile(
      context,
      iFace,
      context.polkadotApi.tx.parachainStaking.setParachainBondAccount(GENESIS_ACCOUNT)
    );

    // propose
    await sendPrecompileTx(
      context,
      ADDRESS_DEMO_PRECOMPILE,
      SELECTORS,
      GENESIS_ACCOUNT,
      GENESIS_ACCOUNT_PRIVATE_KEY,
      "propose",
      [encodedHash, numberToHex(Number(PROPOSAL_AMOUNT))]
    );

    // second
    await sendPrecompileTx(
      context,
      ADDRESS_DEMO_PRECOMPILE,
      SELECTORS,
      ALITH,
      ALITH_PRIV_KEY,
      "second",
      [numberToHex(0), numberToHex(1000)]
    );
  });
  it("check enactment period", async function () {
    // enactmentPeriod
    expect(enactmentPeriod.toHuman()).to.equal("7,200");
  });
  it("check voting Period", async function () {
    // votingPeriod
    expect(votingPeriod.toHuman()).to.equal("36,000");
  });
  it("vote", async function () {
    this.timeout(2000000);
    // let Launchperiod elapse to turn the proposal into a referendum
    // launchPeriod minus the 3 blocks that already elapsed
    for (let i = 0; i < 7200 - 3; i++) {
      await context.createBlock();
    }
    // vote
    await sendPrecompileTx(
      context,
      ADDRESS_DEMO_PRECOMPILE,
      SELECTORS,
      ALITH,
      ALITH_PRIV_KEY,
      "standard_vote",
      [numberToHex(0), "0x01", numberToHex(Number(VOTE_AMOUNT)), numberToHex(1)]
    );

    // referendumInfoOf
    const referendumInfoOf = await context.polkadotApi.query.democracy.referendumInfoOf(0);
    console.log("referendumInfoOf.toHuman() ", referendumInfoOf.toHuman());
    expect((referendumInfoOf.toHuman() as any).Ongoing.proposalHash).to.equal(encodedHash);
    expect((referendumInfoOf.toHuman() as any).Ongoing.tally.ayes).to.equal(
      "10,000,000,000,000,000,000"
    );
    expect((referendumInfoOf.toHuman() as any).Ongoing.tally.turnout).to.equal(
      "10,000,000,000,000,000,000"
    );

    // let votePeriod + enactmentPeriod elapse to turn the proposal into a referendum
    for (let i = 0; i < Number(votingPeriod) + Number(enactmentPeriod) + 10; i++) {
      await context.createBlock();
    }
    let parachainBondInfo = await context.polkadotApi.query.parachainStaking.parachainBondInfo();
    expect(parachainBondInfo.toHuman()["account"]).to.equal(GENESIS_ACCOUNT);
  });
});
Example #22
Source File: App.tsx    From interbtc-ui with Apache License 2.0 4 votes vote down vote up
App = (): JSX.Element => {
  const {
    bridgeLoaded,
    address,
    wrappedTokenBalance,
    wrappedTokenTransferableBalance,
    collateralTokenBalance,
    collateralTokenTransferableBalance,
    governanceTokenBalance,
    governanceTokenTransferableBalance,
    prices
  } = useSelector((state: StoreType) => state.general);
  // eslint-disable-next-line max-len
  const [bridgeStatus, setBridgeStatus] = React.useState(STATUSES.IDLE); // TODO: `bridgeLoaded` should be based on enum instead of boolean
  const dispatch = useDispatch();

  // Loads the main bridge API - connection to the bridge
  const loadBridge = React.useCallback(async (): Promise<void> => {
    try {
      setBridgeStatus(STATUSES.PENDING);
      window.bridge = await createInterBtcApi(constants.PARACHAIN_URL, constants.BITCOIN_NETWORK);
      dispatch(isBridgeLoaded(true));
      setBridgeStatus(STATUSES.RESOLVED);
    } catch (error) {
      toast.warn('Unable to connect to the BTC-Parachain.');
      console.log('[loadBridge] error.message => ', error.message);
      setBridgeStatus(STATUSES.REJECTED);
    }
  }, [dispatch]);

  // Loads the connection to the faucet - only for testnet purposes
  const loadFaucet = React.useCallback(async (): Promise<void> => {
    try {
      window.faucet = new FaucetClient(window.bridge.api, constants.FAUCET_URL);
      dispatch(isFaucetLoaded(true));
    } catch (error) {
      console.log('[loadFaucet] error.message => ', error.message);
    }
  }, [dispatch]);

  // Loads the bridge
  React.useEffect(() => {
    if (bridgeLoaded) return; // Not necessary but for more clarity
    if (bridgeStatus !== STATUSES.IDLE) return;

    (async () => {
      try {
        await loadBridge();
      } catch (error) {
        console.log('[App React.useEffect 7] error.message => ', error.message);
      }
    })();
  }, [loadBridge, bridgeLoaded, bridgeStatus]);

  // Loads the faucet
  React.useEffect(() => {
    if (!bridgeLoaded) return;
    if (process.env.REACT_APP_BITCOIN_NETWORK === BitcoinNetwork.Mainnet) return;

    (async () => {
      try {
        await loadFaucet();
      } catch (error) {
        console.log('[App React.useEffect 8] error.message => ', error.message);
      }
    })();
  }, [bridgeLoaded, loadFaucet]);

  // Maybe loads the vault client - only if the current address is also registered as a vault
  React.useEffect(() => {
    if (!bridgeLoaded) return;
    if (!address) return;

    const id = window.bridge.api.createType(ACCOUNT_ID_TYPE_NAME, address);

    (async () => {
      try {
        dispatch(isVaultClientLoaded(false));
        const vault = await window.bridge.vaults.get(id, COLLATERAL_TOKEN_ID_LITERAL);
        dispatch(isVaultClientLoaded(!!vault));
      } catch (error) {
        // TODO: should add error handling
        console.log('[App React.useEffect 1] error.message => ', error.message);
      }
    })();
  }, [bridgeLoaded, address, dispatch]);

  // Initializes data on app bootstrap
  React.useEffect(() => {
    if (!dispatch) return;
    if (!bridgeLoaded) return;

    (async () => {
      try {
        const [
          totalWrappedTokenAmount,
          totalLockedCollateralTokenAmount,
          totalGovernanceTokenAmount,
          btcRelayHeight,
          bitcoinHeight,
          state
        ] = await Promise.all([
          window.bridge.tokens.total(WRAPPED_TOKEN),
          window.bridge.tokens.total(COLLATERAL_TOKEN),
          window.bridge.tokens.total(GOVERNANCE_TOKEN),
          window.bridge.btcRelay.getLatestBlockHeight(),
          window.bridge.electrsAPI.getLatestBlockHeight(),
          window.bridge.system.getStatusCode()
        ]);

        const parachainStatus = (state: SecurityStatusCode) => {
          if (state.isError) {
            return ParachainStatus.Error;
          } else if (state.isRunning) {
            return ParachainStatus.Running;
          } else if (state.isShutdown) {
            return ParachainStatus.Shutdown;
          } else {
            return ParachainStatus.Loading;
          }
        };

        dispatch(
          initGeneralDataAction(
            totalWrappedTokenAmount,
            totalLockedCollateralTokenAmount,
            totalGovernanceTokenAmount,
            Number(btcRelayHeight),
            bitcoinHeight,
            parachainStatus(state)
          )
        );
      } catch (error) {
        // TODO: should add error handling
        console.log('[App React.useEffect 2] error.message => ', error.message);
      }
    })();
  }, [dispatch, bridgeLoaded]);

  // Loads the address for the currently selected account
  React.useEffect(() => {
    if (!dispatch) return;
    if (!bridgeLoaded) return;

    const trySetDefaultAccount = () => {
      if (constants.DEFAULT_ACCOUNT_SEED) {
        const keyring = new Keyring({ type: 'sr25519', ss58Format: constants.SS58_FORMAT });
        const defaultAccountKeyring = keyring.addFromUri(constants.DEFAULT_ACCOUNT_SEED as string);
        window.bridge.setAccount(defaultAccountKeyring);
        dispatch(changeAddressAction(defaultAccountKeyring.address));
      }
    };

    (async () => {
      try {
        const theExtensions = await web3Enable(APP_NAME);
        if (theExtensions.length === 0) {
          trySetDefaultAccount();
          return;
        }

        dispatch(setInstalledExtensionAction(theExtensions.map((extension) => extension.name)));

        // TODO: load accounts just once
        const accounts = await web3Accounts({ ss58Format: constants.SS58_FORMAT });
        const matchedAccount = accounts.find((account) => account.address === address);

        if (matchedAccount) {
          const { signer } = await web3FromAddress(address);
          window.bridge.setAccount(address, signer);
          dispatch(changeAddressAction(address));
        } else {
          dispatch(changeAddressAction(''));
          window.bridge.removeAccount();
        }
      } catch (error) {
        // TODO: should add error handling
        console.log('[App React.useEffect 3] error.message => ', error.message);
      }
    })();
  }, [address, bridgeLoaded, dispatch]);

  // Subscribes to balances
  React.useEffect(() => {
    if (!dispatch) return;
    if (!bridgeLoaded) return;
    if (!address) return;

    let unsubscribeFromCollateral: () => void;
    let unsubscribeFromWrapped: () => void;
    let unsubscribeFromGovernance: () => void;

    (async () => {
      try {
        unsubscribeFromCollateral = await window.bridge.tokens.subscribeToBalance(
          COLLATERAL_TOKEN,
          address,
          (_: string, balance: ChainBalance<CollateralUnit>) => {
            if (!balance.free.eq(collateralTokenBalance)) {
              dispatch(updateCollateralTokenBalanceAction(balance.free));
            }
            if (!balance.transferable.eq(collateralTokenTransferableBalance)) {
              dispatch(updateCollateralTokenTransferableBalanceAction(balance.transferable));
            }
          }
        );
      } catch (error) {
        console.log('[App React.useEffect 4] error.message => ', error.message);
      }
    })();

    (async () => {
      try {
        unsubscribeFromWrapped = await window.bridge.tokens.subscribeToBalance(
          WRAPPED_TOKEN,
          address,
          (_: string, balance: ChainBalance<BitcoinUnit>) => {
            if (!balance.free.eq(wrappedTokenBalance)) {
              dispatch(updateWrappedTokenBalanceAction(balance.free));
            }
            if (!balance.transferable.eq(wrappedTokenTransferableBalance)) {
              dispatch(updateWrappedTokenTransferableBalanceAction(balance.transferable));
            }
          }
        );
      } catch (error) {
        console.log('[App React.useEffect 5] error.message => ', error.message);
      }
    })();

    (async () => {
      try {
        unsubscribeFromGovernance = await window.bridge.tokens.subscribeToBalance(
          GOVERNANCE_TOKEN,
          address,
          (_: string, balance: ChainBalance<GovernanceUnit>) => {
            if (!balance.free.eq(governanceTokenBalance)) {
              dispatch(updateGovernanceTokenBalanceAction(balance.free));
            }
            if (!balance.transferable.eq(governanceTokenTransferableBalance)) {
              dispatch(updateGovernanceTokenTransferableBalanceAction(balance.transferable));
            }
          }
        );
      } catch (error) {
        console.log('[App React.useEffect 6] error.message => ', error.message);
      }
    })();

    return () => {
      if (unsubscribeFromCollateral) {
        unsubscribeFromCollateral();
      }
      if (unsubscribeFromWrapped) {
        unsubscribeFromWrapped();
      }
      if (unsubscribeFromGovernance) {
        unsubscribeFromGovernance();
      }
    };
  }, [
    dispatch,
    bridgeLoaded,
    address,
    wrappedTokenBalance,
    wrappedTokenTransferableBalance,
    collateralTokenBalance,
    collateralTokenTransferableBalance,
    governanceTokenBalance,
    governanceTokenTransferableBalance
  ]);

  // Color schemes according to Interlay vs. Kintsugi
  React.useEffect(() => {
    if (process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT) {
      document.documentElement.classList.add(CLASS_NAMES.LIGHT);
      document.documentElement.classList.remove(CLASS_NAMES.DARK);
      document.body.classList.add('text-interlayTextPrimaryInLightMode');
      document.body.classList.add('bg-interlayHaiti-50');
      document.body.classList.add('theme-interlay');
    }

    // MEMO: should check dark mode as well
    if (process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA) {
      // MEMO: inspired by https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually
      document.documentElement.classList.add(CLASS_NAMES.DARK);
      document.documentElement.classList.remove(CLASS_NAMES.LIGHT);
      document.body.classList.add('dark:text-kintsugiTextPrimaryInDarkMode');
      document.body.classList.add('dark:bg-kintsugiMidnight-900');
      document.body.classList.add('theme-kintsugi');
    }
  }, []);

  // Keeps fetching live data prices
  const { error: pricesError } = useQuery(
    PRICES_URL,
    async () => {
      const response = await fetch(PRICES_URL);
      if (!response.ok) {
        throw new Error('Network response for prices was not ok.');
      }

      const newPrices = await response.json();
      // Update the store only if the price is actually changed
      if (
        newPrices.bitcoin?.usd !== prices.bitcoin?.usd ||
        newPrices[RELAY_CHAIN_NAME]?.usd !== prices.collateralToken?.usd ||
        newPrices[BRIDGE_PARACHAIN_NAME]?.usd !== prices.governanceToken?.usd
      ) {
        dispatch(
          updateOfPricesAction({
            bitcoin: newPrices.bitcoin,
            collateralToken: newPrices[RELAY_CHAIN_NAME],
            governanceToken: newPrices[BRIDGE_PARACHAIN_NAME]
          })
        );
      }
    },
    { refetchInterval: 60000 }
  );
  useErrorHandler(pricesError);

  return (
    <>
      <InterlayHelmet />
      <ToastContainer position='top-right' autoClose={5000} hideProgressBar={false} />
      <Layout>
        <Route
          render={({ location }) => (
            <React.Suspense fallback={<FullLoadingSpinner />}>
              <Switch location={location}>
                <Route exact path={PAGES.VAULTS}>
                  <Vaults />
                </Route>
                <Route exact path={PAGES.VAULT}>
                  <Vault />
                </Route>
                <Route path={PAGES.VAULT}>
                  <Vaults />
                </Route>
                <Route path={PAGES.DASHBOARD}>
                  <Dashboard />
                </Route>
                <Route path={PAGES.STAKING}>
                  <Staking />
                </Route>
                <Route path={PAGES.TRANSACTIONS}>
                  <Transactions />
                </Route>
                <Route path={PAGES.BRIDGE}>
                  <Bridge />
                </Route>
                <Route path={PAGES.TRANSFER}>
                  <Transfer />
                </Route>
                <Redirect exact from={PAGES.HOME} to={PAGES.BRIDGE} />
                <Route path='*'>
                  <NoMatch />
                </Route>
              </Switch>
            </React.Suspense>
          )}
        />
      </Layout>
    </>
  );
}
Example #23
Source File: index.ts    From parachain-launch with Apache License 2.0 4 votes vote down vote up
generateRelaychainGenesisFile = (config: Config, path: string, output: string) => {
  const relaychain = config.relaychain;

  if (!relaychain) {
    return fatal('Missing relaychain');
  }
  if (!relaychain.chain) {
    return fatal('Missing relaychain.chain');
  }
  if (!relaychain.image) {
    return fatal('Missing relaychain.image');
  }

  const spec = getChainspec(relaychain.image, relaychain.chain);

  // clear authorities
  const runtime = spec.genesis.runtime.runtime_genesis_config || spec.genesis.runtime;

  const sessionKeys = runtime.session.keys;
  sessionKeys.length = 0;

  // add authorities from config
  const keyring = new Keyring();
  for (const { name } of config.relaychain.nodes) {
    const srAcc = keyring.createFromUri(`//${_.startCase(name)}`, undefined, 'sr25519');
    const srStash = keyring.createFromUri(`//${_.startCase(name)}//stash`, undefined, 'sr25519');
    const edAcc = keyring.createFromUri(`//${_.startCase(name)}`, undefined, 'ed25519');
    const ecAcc = keyring.createFromUri(`//${_.startCase(name)}`, undefined, 'ecdsa');

    const key = [
      srStash.address,
      srStash.address,
      {
        grandpa: edAcc.address,
        babe: srAcc.address,
        im_online: srAcc.address,
        parachain_validator: srAcc.address,
        authority_discovery: srAcc.address,
        para_validator: srAcc.address,
        para_assignment: srAcc.address,
        beefy: encodeAddress(ecAcc.publicKey),
      },
    ];

    sessionKeys.push(key);
  }

  // additional patches
  if (config.relaychain.runtimeGenesisConfig) {
    const hrmp = config.relaychain.runtimeGenesisConfig.hrmp;
    if (hrmp) {
      hrmp.preopenHrmpChannels = hrmp.preopenHrmpChannels.map((channel) => {
        if (!Array.isArray(channel)) {
          return [channel.sender, channel.recipient, channel.maxCapacity, channel.maxMessageSize];
        } else {
          return channel;
        }
      });
    }
    _.merge(runtime, config.relaychain.runtimeGenesisConfig);
  }

  // genesis parachains
  for (const parachain of config.parachains) {
    const { wasm, state } = exportParachainGenesis(parachain, output);
    if (!parachain.id) {
      return fatal('Missing parachains[].id');
    }
    const para = [
      parachain.id,
      {
        genesis_head: state,
        validation_code: wasm,
        parachain: parachain.parachain,
      },
    ];
    runtime.paras.paras.push(para);
  }

  const tmpfile = `${shell.tempdir()}/${config.relaychain.chain}.json`;
  fs.writeFileSync(tmpfile, jsonStringify(spec));

  exec(
    `docker run --rm -v "${tmpfile}":/${config.relaychain.chain}.json ${config.relaychain.image} build-spec --raw --chain=/${config.relaychain.chain}.json --disable-default-bootnode > ${path}`
  );

  shell.rm(tmpfile);

  console.log('Relaychain genesis generated at', path);
}
Example #24
Source File: skip-bug-ethereum-parent-hash.ts    From moonbeam with GNU General Public License v3.0 4 votes vote down vote up
describeParachain(
  `Runtime ${runtimeVersion} migration`,
  {
    parachain: {
      chain: "moonbase-local",
      runtime: "runtime-1103",
      binary: "v0.19.1",
    },
    relaychain: {
      binary: "v0.9.13",
    },
  },
  (context) => {
    it("have proper parent hash", async function () {
      // Expected to take 4 blocks to setup + 10 blocks for upgrade + 4 blocks to check =>
      // ~300000 + init 50000 + error marging 150000
      this.timeout(500000);

      const keyring = new Keyring({ type: "ethereum" });
      const alith = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
      const baltathar = await keyring.addFromUri(BALTATHAR_PRIV_KEY, null, "ethereum");

      let baltatharNonce = await context.web3.eth.getTransactionCount(baltathar.address);

      // It takes 10 blocks
      let hasMoreBlockPassed = false;
      const runtimePromise = context
        .upgradeRuntime(alith, "moonbase", runtimeVersion)
        .then(async (blockNumber) => {
          context.waitBlocks(3).then(() => {
            hasMoreBlockPassed = true;
          });
          return blockNumber;
        });

      // It takes 5 blocks for the runtime, however we need to send before to have
      // the extrinsics included

      while (!hasMoreBlockPassed) {
        const tx = await context.web3.eth.accounts.signTransaction(
          {
            from: baltathar.address,
            to: alith.address,
            value: Web3.utils.toWei("1", "ether"),
            gasPrice: Web3.utils.toWei("1", "Gwei"),
            gas: "0x100000",
            nonce: baltatharNonce++,
          },
          BALTATHAR_PRIV_KEY
        );

        await customWeb3Request(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]);
        await new Promise((resolve) => setTimeout(resolve, 12000));
      }

      for (let i = 1; i < context.blockNumber - 1; i++) {
        console.log(
          `#${i} ${(await context.web3.eth.getBlock(i)).parentHash} ${
            (
              await context.polkadotApiParaone.rpc.state.getRuntimeVersion(
                await context.polkadotApiParaone.rpc.chain.getBlockHash(i)
              )
            ).specVersion
          } (${
            (
              await context.polkadotApiParaone.rpc.chain.getBlock(
                await context.polkadotApiParaone.rpc.chain.getBlockHash(i)
              )
            ).block.extrinsics.length
          } ext)`
        );
      }
      process.stdout.write(`!!!!! \n`);

      expect((await context.web3.eth.getBlock((await runtimePromise) + 1)).parentHash).to.be.string(
        "0x0000000000000000000000000000000000000000000000000000000000000000"
      ); // new runtime only allow 50 bottom
      process.stdout.write(`✅\n`);

      process.stdout.write("Waiting extra block being produced...");
      await context.waitBlocks(2); // Make sure the new runtime is producing blocks
      process.stdout.write(`✅ total ${context.blockNumber} block produced\n`);
    });
  }
);
Example #25
Source File: vaults.test.ts    From interbtc-api with Apache License 2.0 4 votes vote down vote up
describe("vaultsAPI", () => {
    let oracleAccount: KeyringPair;
    let vault_to_liquidate: KeyringPair;
    let vault_to_ban: KeyringPair;
    let vault_1: KeyringPair;
    let vault_1_id: InterbtcPrimitivesVaultId;
    let vault_2: KeyringPair;
    let vault_2_id: InterbtcPrimitivesVaultId;
    let vault_3: KeyringPair;
    let vault_3_id: InterbtcPrimitivesVaultId;
    let api: ApiPromise;
    let bitcoinCoreClient: BitcoinCoreClient;

    let wrappedCurrency: WrappedCurrency;
    let collateralCurrency: CollateralCurrency;
    let governanceCurrency: Currency<GovernanceUnit>;

    let interBtcAPI: InterBtcApi;
    let oracleInterBtcAPI: InterBtcApi;

    before(async () => {
        api = await createSubstrateAPI(PARACHAIN_ENDPOINT);
        const keyring = new Keyring({ type: "sr25519" });
        oracleAccount = keyring.addFromUri(ORACLE_URI);
        interBtcAPI = new DefaultInterBtcApi(api, "regtest", undefined, ESPLORA_BASE_PATH);
        oracleInterBtcAPI = new DefaultInterBtcApi(api, "regtest", oracleAccount, ESPLORA_BASE_PATH);
        wrappedCurrency = interBtcAPI.getWrappedCurrency();
        governanceCurrency = interBtcAPI.getGovernanceCurrency();
        collateralCurrency = getCorrespondingCollateralCurrency(governanceCurrency);
        vault_1 = keyring.addFromUri(VAULT_1_URI);
        vault_1_id = newVaultId(api, vault_1.address, collateralCurrency, wrappedCurrency);
        vault_2 = keyring.addFromUri(VAULT_2_URI);
        vault_2_id = newVaultId(api, vault_2.address, collateralCurrency, wrappedCurrency);
        vault_3 = keyring.addFromUri(VAULT_3_URI);
        vault_3_id = newVaultId(api, vault_3.address, collateralCurrency, wrappedCurrency);
        vault_to_ban = keyring.addFromUri(VAULT_TO_BAN_URI);
        vault_to_liquidate = keyring.addFromUri(VAULT_TO_LIQUIDATE_URI);


        bitcoinCoreClient = new BitcoinCoreClient(
            BITCOIN_CORE_NETWORK,
            BITCOIN_CORE_HOST,
            BITCOIN_CORE_USERNAME,
            BITCOIN_CORE_PASSWORD,
            BITCOIN_CORE_PORT,
            BITCOIN_CORE_WALLET
        );
    });

    after(() => {
        return api.disconnect();
    });

    afterEach(() => {
        // discard any stubbed methods after each test
        sinon.restore();
    });

    function vaultIsATestVault(vaultAddress: string): boolean {
        return vaultAddress === vault_2.address ||
            vaultAddress === vault_1.address ||
            vaultAddress === vault_3.address ||
            vaultAddress === vault_to_ban.address ||
            vaultAddress === vault_to_liquidate.address;
    }

    // FIXME: this should be tested in a way that in doesn't use magic numbers
    it("should get issuable", async () => {
        const issuableInterBTC = await interBtcAPI.vaults.getTotalIssuableAmount();
        const minExpectedIssuableInterBTC = newMonetaryAmount(0.002, wrappedCurrency, true);
        assert.isTrue(issuableInterBTC.gte(minExpectedIssuableInterBTC), `Issuable ${issuableInterBTC.toHuman()}`);
    });

    it("should get the required collateral for the vault", async () => {
        const collateralCurrency = currencyIdToMonetaryCurrency(vault_1_id.currencies.collateral) as Currency<CollateralUnit>;
        const requiredCollateralForVault =
            await interBtcAPI.vaults.getRequiredCollateralForVault(vault_1_id.accountId, collateralCurrency);

        const vault = await interBtcAPI.vaults.get(vault_1_id.accountId, currencyIdToLiteral(vault_1_id.currencies.collateral));

        // The numeric value of the required collateral should be greater than that of issued tokens.
        // e.g. we require `0.8096` KSM for `0.00014` kBTC
        assert.isTrue(requiredCollateralForVault.toBig().gt(vault.getBackedTokens().toBig()));
    });

    // WARNING: this test is not idempotent
    it("should deposit and withdraw collateral", async () => {
        const prevAccount = interBtcAPI.account;
        interBtcAPI.setAccount(vault_1);
        const amount = newMonetaryAmount(100, collateralCurrency as Currency<CollateralUnit>, true);
        const collateralCurrencyIdLiteral = tickerToCurrencyIdLiteral(collateralCurrency.ticker) as CollateralIdLiteral;

        const collateralizationBeforeDeposit =
            await interBtcAPI.vaults.getVaultCollateralization(newAccountId(api, vault_1.address), collateralCurrencyIdLiteral);
        await interBtcAPI.vaults.depositCollateral(amount);
        const collateralizationAfterDeposit =
            await interBtcAPI.vaults.getVaultCollateralization(newAccountId(api, vault_1.address), collateralCurrencyIdLiteral);
        if (collateralizationBeforeDeposit === undefined || collateralizationAfterDeposit == undefined) {
            throw new Error("Collateralization is undefined");
        }
        assert.isTrue(
            collateralizationAfterDeposit.gt(collateralizationBeforeDeposit),
            `Depositing did not increase collateralization,
            expected ${collateralizationAfterDeposit} greater than ${collateralizationBeforeDeposit}`
        );

        await interBtcAPI.vaults.withdrawCollateral(amount);
        const collateralizationAfterWithdrawal =
            await interBtcAPI.vaults.getVaultCollateralization(newAccountId(api, vault_1.address), collateralCurrencyIdLiteral);
        if (collateralizationAfterWithdrawal === undefined) {
            throw new Error("Collateralization is undefined");
        }
        assert.isTrue(
            collateralizationAfterDeposit.gt(collateralizationAfterWithdrawal),
            `Withdrawing did not decrease collateralization, expected
            ${collateralizationAfterDeposit} greater than ${collateralizationAfterWithdrawal}`
        );
        assert.equal(
            collateralizationBeforeDeposit.toString(), collateralizationAfterWithdrawal.toString(),
            "Collateralization after identical deposit and withdrawal changed"
        );
        if (prevAccount) {
            interBtcAPI.setAccount(prevAccount);
        }
    });

    it("should getPremiumRedeemVaults after a price crash", async () => {
        const collateralCurrencyIdLiteral = currencyIdToLiteral(vault_3_id.currencies.collateral) as CollateralIdLiteral;
        const vault = await interBtcAPI.vaults.get(vault_3_id.accountId, collateralCurrencyIdLiteral);
        let issuableAmount = await vault.getIssuableTokens();
        // TODO: Look into why requesting the full issuable amount fails, and remove the line below
        issuableAmount = issuableAmount.mul(0.9);
        await issueSingle(interBtcAPI, bitcoinCoreClient, oracleAccount, issuableAmount, vault_3_id);

        const currentVaultCollateralization =
            await interBtcAPI.vaults.getVaultCollateralization(newAccountId(api, vault_3.address), collateralCurrencyIdLiteral);
        if (currentVaultCollateralization === undefined) {
            throw new Error("Collateralization is undefined");
        }

        const collateralCurrencyTyped = collateralCurrency as Currency<CollateralUnit>;

        // The factor to adjust the exchange rate by. Calculated such that the resulting collateralization
        // will be 90% of the premium redeem threshold. (e.g. 1.35 * 90% = 1.215)
        const premiumRedeemThreshold = await interBtcAPI.vaults.getPremiumRedeemThreshold(collateralCurrency);
        const modifyExchangeRateBy = premiumRedeemThreshold.mul(0.9).div(currentVaultCollateralization);

        const initialExchangeRate = await interBtcAPI.oracle.getExchangeRate(collateralCurrencyTyped);
        // crash the exchange rate so that the vault falls below the premium redeem threshold
        const exchangeRateValue = initialExchangeRate.toBig().div(modifyExchangeRateBy);
        const mockExchangeRate = new ExchangeRate<
            Bitcoin,
            BitcoinUnit,
            typeof collateralCurrencyTyped,
            typeof collateralCurrencyTyped.units
        >(Bitcoin, collateralCurrencyTyped, exchangeRateValue);

        // stub the oracle API to always return the new exchange rate
        const stub = sinon.stub(interBtcAPI.oracle, "getExchangeRate")
            .withArgs(sinon.match.any)
            .returns(Promise.resolve(mockExchangeRate as any)); // "as any" to help eslint play nicely

        const premiumRedeemVaults = await interBtcAPI.vaults.getPremiumRedeemVaults();

        // Check that the stub has indeed been called at least once 
        // If not, code has changed and our assumptions when mocking the oracle API are no longer valid
        sinon.assert.called(stub);

        // real assertions here
        assert.equal(premiumRedeemVaults.size, 1);
        assert.equal(
            encodeVaultId(premiumRedeemVaults.keys().next().value),
            encodeVaultId(vault_3_id),
            "Premium redeem vault is not the expected one"
        );

        const premiumRedeemAmount = premiumRedeemVaults.values().next().value;
        assert.isTrue(
            premiumRedeemAmount.gte(issuableAmount),
            "Amount available for premium redeem should be higher"
        );
    }).timeout(5 * 60000);

    it("should getLiquidationCollateralThreshold", async () => {
        const threshold = await interBtcAPI.vaults.getLiquidationCollateralThreshold(collateralCurrency);
        assert.equal(threshold.toString(), "1.1");
    });

    it("should getPremiumRedeemThreshold", async () => {
        const threshold = await interBtcAPI.vaults.getPremiumRedeemThreshold(collateralCurrency);
        assert.equal(threshold.toString(), "1.35");
    });

    it("should select random vault for issue", async () => {
        const randomVault = await interBtcAPI.vaults.selectRandomVaultIssue(newMonetaryAmount(0, wrappedCurrency));
        assert.isTrue(vaultIsATestVault(randomVault.accountId.toHuman()));
    });

    it("should fail if no vault for issuing is found", async () => {
        assert.isRejected(interBtcAPI.vaults.selectRandomVaultIssue(newMonetaryAmount(9000000, wrappedCurrency, true)));
    });

    it("should select random vault for redeem", async () => {
        const randomVault = await interBtcAPI.vaults.selectRandomVaultRedeem(newMonetaryAmount(0, wrappedCurrency));
        assert.isTrue(vaultIsATestVault(randomVault.accountId.toHuman()));
    });

    it("should fail if no vault for redeeming is found", async () => {
        const amount = newMonetaryAmount(9000000, wrappedCurrency, true);
        assert.isRejected(interBtcAPI.vaults.selectRandomVaultRedeem(amount));
    });

    it("should fail to get vault collateralization for vault with zero collateral", async () => {
        const vault1Id = newAccountId(api, vault_1.address);
        const collateralCurrencyIdLiteral = tickerToCurrencyIdLiteral(collateralCurrency.ticker) as CollateralIdLiteral;
        assert.isRejected(interBtcAPI.vaults.getVaultCollateralization(vault1Id, collateralCurrencyIdLiteral));
    });

    it("should get the issuable InterBtc for a vault", async () => {
        const collateralCurrencyIdLiteral = currencyIdToLiteral(vault_1_id.currencies.collateral) as CollateralIdLiteral;
        const vault = await interBtcAPI.vaults.get(vault_1_id.accountId, collateralCurrencyIdLiteral);
        const issuableTokens = await vault.getIssuableTokens();
        assert.isTrue(issuableTokens.gt(newMonetaryAmount(0, wrappedCurrency)));
    });

    it("should get the issuable InterBtc", async () => {
        const issuableInterBtc = await interBtcAPI.vaults.getTotalIssuableAmount();
        assert.isTrue(issuableInterBtc.gt(newMonetaryAmount(0, wrappedCurrency)));
    });

    // TODO: revisit after next publish why intrReward is always zero
    it.skip("should getFees", async () => {
        const vault1Id = newAccountId(api, vault_1.address);
        const feesWrapped = await interBtcAPI.vaults.getWrappedReward(
            vault1Id,
            currencyIdToLiteral(vault_1_id.currencies.collateral) as CollateralIdLiteral,
            currencyIdToLiteral(vault_1_id.currencies.wrapped) as WrappedIdLiteral
        );
        assert.isTrue(feesWrapped.gt(newMonetaryAmount(0, wrappedCurrency)));

        const intrReward = await interBtcAPI.vaults.getGovernanceReward(
            vault1Id,
            currencyIdToLiteral(vault_1_id.currencies.collateral) as CollateralIdLiteral,
            tickerToCurrencyIdLiteral(governanceCurrency.ticker) as GovernanceIdLiteral
        );
        assert.isTrue(feesWrapped.gt(newMonetaryAmount(0, wrappedCurrency)));
        assert.isTrue(intrReward.gt(newMonetaryAmount(0, governanceCurrency)));
    });

    it("should getAPY", async () => {
        const apy =
            await interBtcAPI.vaults.getAPY(
                newAccountId(api, vault_1.address), currencyIdToLiteral(vault_1_id.currencies.collateral) as CollateralIdLiteral
            );
        const apyBig = new Big(apy);
        const apyBenchmark = new Big("0");
        assert.isTrue(apyBig.gte(apyBenchmark));
    });

    it("should getPunishmentFee", async () => {
        const punishmentFee = await interBtcAPI.vaults.getPunishmentFee();
        assert.equal(punishmentFee.toString(), "0.1");
    });

    it("should get vault list", async () => {
        const vaults = (await interBtcAPI.vaults.list()).map(vault => vault.id.toHuman());
        assert.isAbove(vaults.length, 0, "Vault list should not be empty");
    });

    it("should disable and enable issuing with vault", async () => {
        const assertVaultStatus = async (id: InterbtcPrimitivesVaultId, expectedStatus: VaultStatusExt) => {
            const collateralCurrencyIdLiteral = currencyIdToLiteral(id.currencies.collateral);
            const { status } = await interBtcAPI.vaults.get(id.accountId, collateralCurrencyIdLiteral);
            const assertionMessage = `Vault with id ${id.toString()} was expected to have 
                    status: ${vaultStatusToLabel(expectedStatus)}, but got status: ${vaultStatusToLabel(status)}`;

            assert.isTrue(status === expectedStatus, assertionMessage);
        };
        const ACCEPT_NEW_ISSUES = true;
        const REJECT_NEW_ISSUES = false;


        // Check that vault 1 is active.
        await assertVaultStatus(vault_1_id, VaultStatusExt.Active);
        // Disables vault 1 which is active.
        await interBtcAPI.vaults.toggleIssueRequests(vault_1_id, REJECT_NEW_ISSUES);
        // Check that vault 1 is inactive.
        await assertVaultStatus(vault_1_id, VaultStatusExt.Inactive);
        // Re-enable issuing with vault 1.
        await interBtcAPI.vaults.toggleIssueRequests(vault_1_id, ACCEPT_NEW_ISSUES);
        // Check that vault 1 is again active.
        await assertVaultStatus(vault_1_id, VaultStatusExt.Active);
    });


});
Example #26
Source File: test-staking.ts    From moonbeam with GNU General Public License v3.0 4 votes vote down vote up
async function test() {
  await start("config_moonbeam_staking.json");
  const WS_PORT = 36946;
  const wsProviderUrl = `ws://localhost:${WS_PORT}`;

  const wsProvider = new WsProvider(wsProviderUrl);
  const polkadotApi = await ApiPromise.create({
    provider: wsProvider,
    typesBundle: typesBundlePre900 as any,
  });

  // subscribe to all new headers (with extended info)
  let lastBlock = Date.now();
  polkadotApi.derive.chain.subscribeNewHeads((header) => {
    console.log(
      `New Block: #${header.number}: ${header.author}, time since last block: ${
        (Date.now() - lastBlock) / 1000
      } sec`
    );
    lastBlock = Date.now();
  });

  // Balance
  const account = await polkadotApi.query.system.account(ETHAN);
  assert(
    account.data.free.toString() === DEFAULT_GENESIS_BALANCE.toString(),
    "wrong balance for Ethan, dif: " + (Number(DEFAULT_GENESIS_BALANCE) - Number(account.data.free))
  );

  // Nominators
  const nominators = await polkadotApi.query.parachainStaking.nominatorState(GERALD);
  assert(nominators.toHuman() === null, "there should be no nominator");

  // Validators
  const validators = await polkadotApi.query.parachainStaking.selectedCandidates();
  assert(validators.toHuman()[0] === GERALD, "Gerald is not a validator");
  assert(validators.toHuman()[1] === FAITH, "Faith is not a validator");

  // Candidates
  const candidates = await polkadotApi.query.parachainStaking.candidatePool();
  assert(candidates.toHuman()[0].owner === GERALD, "Gerald is not a candidates");
  assert(candidates.toHuman()[1].owner === FAITH, "Faith is not a candidates");
  assert(candidates.toHuman()[0].amount === STAKING_AMOUNT, "Gerald has wrong staking amount");
  assert(candidates.toHuman()[1].amount === STAKING_AMOUNT, "Faith has wrong staking amount");

  // Join Candidates
  const keyring = new Keyring({ type: "ethereum" });
  const ethan = await keyring.addFromUri(ETHAN_PRIVKEY, null, "ethereum");
  await new Promise<void>(async (res) => {
    const unsub = await polkadotApi.tx.parachainStaking
      .joinCandidates(MIN_GLMR_STAKING)
      .signAndSend(ethan, ({ events = [], status }) => {
        console.log(`Current status is ${status.type}`);
        if (status.isInBlock) {
          console.log(`Transaction included in Block at blockHash ${status.asInBlock}`);

          // Loopcod through Vec<EventRecord> to display all events
          events.forEach(({ phase, event: { data, method, section } }) => {
            console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
          });

          unsub();
          res();
        } else if (status.isFinalized) {
          console.log(`Transaction finalized at blockHash ${status.asFinalized}`);

          // Loopcod through Vec<EventRecord> to display all events
          events.forEach(({ phase, event: { data, method, section } }) => {
            console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
          });

          unsub();
          res();
        }
      });
  });
  let candidatesAfter = await polkadotApi.query.parachainStaking.candidatePool();
  assert(
    (candidatesAfter.toHuman() as { owner: string; amount: string }[]).length === 3,
    "new candidate should have been added"
  );
  assert(
    (candidatesAfter.toHuman() as { owner: string; amount: string }[])[2].owner === ETHAN,
    "new candidate ethan should have been added"
  );
  assert(
    (candidatesAfter.toHuman() as { owner: string; amount: string }[])[2].amount === "1.0000 kUnit",
    "new candidate ethan should have been added (wrong amount)"
  );

  // Candidate bond more
  await new Promise<void>(async (res) => {
    const unsub = await polkadotApi.tx.parachainStaking
      .candidateBondMore(MIN_GLMR_STAKING)
      .signAndSend(ethan, ({ events = [], status }) => {
        console.log(`Current status is ${status.type}`);
        if (status.isInBlock) {
          console.log(`Transaction included in Block at blockHash ${status.asInBlock}`);

          // Loopcod through Vec<EventRecord> to display all events
          events.forEach(({ phase, event: { data, method, section } }) => {
            console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
          });

          unsub();
          res();
        } else if (status.isFinalized) {
          console.log(`Transaction finalized at blockHash ${status.asFinalized}`);

          // Loopcod through Vec<EventRecord> to display all events
          events.forEach(({ phase, event: { data, method, section } }) => {
            console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
          });

          unsub();
          res();
        }
      });
  });
  candidatesAfter = await polkadotApi.query.parachainStaking.candidatePool();
  assert(
    (candidatesAfter.toHuman() as { owner: string; amount: string }[])[2].amount === "2.0000 kUnit",
    "bond should have increased"
  );

  // Candidate bond less
  await new Promise<void>(async (res) => {
    const unsub = await polkadotApi.tx.parachainStaking
      .candidateBondLess(MIN_GLMR_STAKING)
      .signAndSend(ethan, ({ events = [], status }) => {
        console.log(`Current status is ${status.type}`);
        if (status.isInBlock) {
          console.log(`Transaction included in Block at blockHash ${status.asInBlock}`);

          // Loopcod through Vec<EventRecord> to display all events
          events.forEach(({ phase, event: { data, method, section } }) => {
            console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
          });

          unsub();
          res();
        } else if (status.isFinalized) {
          console.log(`Transaction finalized at blockHash ${status.asFinalized}`);

          // Loopcod through Vec<EventRecord> to display all events
          events.forEach(({ phase, event: { data, method, section } }) => {
            console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
          });

          unsub();
          res();
        }
      });
  });
  candidatesAfter = await polkadotApi.query.parachainStaking.candidatePool();
  assert(
    (candidatesAfter.toHuman() as { owner: string; amount: string }[])[2].amount === "1.0000 kUnit",
    "bond should have decreased"
  );

  // Join Nominators
  const keyringAlith = new Keyring({ type: "ethereum" });
  const alith = await keyringAlith.addFromUri(ALITH_PRIVKEY, null, "ethereum");
  await new Promise<void>(async (res) => {
    const unsub = await polkadotApi.tx.parachainStaking
      .nominate(GERALD, MIN_GLMR_NOMINATOR)
      .signAndSend(alith, ({ events = [], status }) => {
        console.log(`Current status is ${status.type}`);
        if (status.isInBlock) {
          console.log(`Transaction included in Block at blockHash ${status.asInBlock}`);

          // Loopcod through Vec<EventRecord> to display all events
          events.forEach(({ phase, event: { data, method, section } }) => {
            console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
          });

          unsub();
          res();
        } else if (status.isFinalized) {
          console.log(`Transaction finalized at blockHash ${status.asFinalized}`);

          // Loop through Vec<EventRecord> to display all events
          events.forEach(({ phase, event: { data, method, section } }) => {
            console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
          });

          unsub();
          res();
        }
      });
  });
  const nominatorsAfter = await polkadotApi.query.parachainStaking.nominatorState(ALITH);
  assert(
    (
      nominatorsAfter.toHuman() as {
        nominations: { owner: string; amount: string }[];
      }
    ).nominations[0].owner === GERALD,
    "nomination didnt go through"
  );

  // Revoke Delegation
  await new Promise<void>(async (res) => {
    const unsub = await polkadotApi.tx.parachainStaking
      .revokeDelegation(GERALD) //TODO: when converting to test add .leaveNominators()
      // that should produce the same behavior
      .signAndSend(alith, ({ events = [], status }) => {
        console.log(`Current status is ${status.type}`);
        if (status.isInBlock) {
          console.log(`Transaction included in Block at blockHash ${status.asInBlock}`);

          // Loopcod through Vec<EventRecord> to display all events
          events.forEach(({ phase, event: { data, method, section } }) => {
            console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
          });

          unsub();
          res();
        } else if (status.isFinalized) {
          console.log(`Transaction finalized at blockHash ${status.asFinalized}`);

          // Loop through Vec<EventRecord> to display all events
          events.forEach(({ phase, event: { data, method, section } }) => {
            console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
          });

          unsub();
          res();
        }
      });
  });
  const nominatorsAfterRevocation = await polkadotApi.query.parachainStaking.nominatorState(ALITH);
  assert(nominatorsAfterRevocation.toHuman() === null, "there should be no nominator");

  console.log("SUCCESS");
}
Example #27
Source File: redeem.test.ts    From interbtc-api with Apache License 2.0 4 votes vote down vote up
describe("redeem", () => {
    let api: ApiPromise;
    let keyring: Keyring;
    let userAccount: KeyringPair;
    const randomBtcAddress = "bcrt1qujs29q4gkyn2uj6y570xl460p4y43ruayxu8ry";
    let bitcoinCoreClient: BitcoinCoreClient;
    let vault_1: KeyringPair;
    let vault_1_id: InterbtcPrimitivesVaultId;
    let vault_2: KeyringPair;
    let vault_2_id: InterbtcPrimitivesVaultId;

    let wrappedCurrency: WrappedCurrency;
    let collateralCurrency: CollateralCurrency;

    let interBtcAPI: InterBtcApi;

    before(async () => {
        api = await createSubstrateAPI(PARACHAIN_ENDPOINT);
        keyring = new Keyring({ type: "sr25519" });
        userAccount = keyring.addFromUri(USER_1_URI);
        interBtcAPI = new DefaultInterBtcApi(api, "regtest", userAccount, ESPLORA_BASE_PATH);
        collateralCurrency = getCorrespondingCollateralCurrency(interBtcAPI.getGovernanceCurrency());
        wrappedCurrency = interBtcAPI.getWrappedCurrency();
        vault_1 = keyring.addFromUri(VAULT_1_URI);
        vault_1_id = newVaultId(api, vault_1.address, collateralCurrency, wrappedCurrency);
        vault_2 = keyring.addFromUri(VAULT_2_URI);
        vault_2_id = newVaultId(api, vault_2.address, collateralCurrency, wrappedCurrency);

        bitcoinCoreClient = new BitcoinCoreClient(
            BITCOIN_CORE_NETWORK,
            BITCOIN_CORE_HOST,
            BITCOIN_CORE_USERNAME,
            BITCOIN_CORE_PASSWORD,
            BITCOIN_CORE_PORT,
            BITCOIN_CORE_WALLET
        );
    });

    after(() => {
        return api.disconnect();
    });

    it("should fail if no account is set", async () => {
        const amount = newMonetaryAmount(10, wrappedCurrency);
        await assert.isRejected(interBtcAPI.redeem.request(amount, randomBtcAddress));
    }).timeout(3 * 60000);

    it("should issue and request redeem", async () => {
        const issueAmount = newMonetaryAmount(0.00005, wrappedCurrency, true);
        const redeemAmount = newMonetaryAmount(0.00003, wrappedCurrency, true);
        await issueAndRedeem(
            interBtcAPI,
            bitcoinCoreClient,
            userAccount,
            vault_1_id,
            issueAmount,
            redeemAmount,
            false,
            ExecuteRedeem.False
        );

        await issueAndRedeem(
            interBtcAPI,
            bitcoinCoreClient,
            userAccount,
            vault_2_id,
            issueAmount,
            redeemAmount,
            false,
            ExecuteRedeem.False
        );
    }).timeout(8 * 60000);

    it("should load existing redeem requests", async () => {
        const redeemRequests = await interBtcAPI.redeem.list();
        assert.isAtLeast(
            redeemRequests.length,
            1,
            "Error in initialization setup. Should have at least 1 issue request"
        );
    });

    // TODO: maybe add this to redeem API
    it("should get redeemBtcDustValue", async () => {
        const dust = await interBtcAPI.api.query.redeem.redeemBtcDustValue();
        assert.equal(dust.toString(), "1000");
    });

    it("should getFeesToPay", async () => {
        const amount = newMonetaryAmount(2, wrappedCurrency, true);
        const feesToPay = await interBtcAPI.redeem.getFeesToPay(amount);
        assert.equal(feesToPay.str.BTC(), "0.01");
    });

    it("should getFeeRate", async () => {
        const feePercentage = await interBtcAPI.redeem.getFeeRate();
        assert.equal(feePercentage.toString(), "0.005");
    });

    it("should getPremiumRedeemFeeRate", async () => {
        const premiumRedeemFee = await interBtcAPI.redeem.getPremiumRedeemFeeRate();
        assert.equal(premiumRedeemFee.toString(), "0.05");
    });

    it("should getCurrentInclusionFee", async () => {
        const currentInclusionFee = await interBtcAPI.redeem.getCurrentInclusionFee();
        assert.isTrue(!currentInclusionFee.isZero());
    });

    it("should getDustValue", async () => {
        const dustValue = await interBtcAPI.redeem.getDustValue();
        assert.equal(dustValue.str.BTC(), "0.00001");
    });

});
Example #28
Source File: test-precompile-democracy.ts    From moonbeam with GNU General Public License v3.0 4 votes vote down vote up
describeDevMoonbeam("Democracy - second proposal", (context) => {
  let genesisAccount: KeyringPair, alith: KeyringPair;
  let encodedHash: `0x${string}`;
  let launchPeriod;
  let iFace: Interface;

  before("Setup genesis account for substrate", async () => {
    const keyring = new Keyring({ type: "ethereum" });
    genesisAccount = await keyring.addFromUri(GENESIS_ACCOUNT_PRIVATE_KEY, null, "ethereum");
    alith = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
    iFace = await deployAndInterfaceContract(context, "Democracy");

    //launchPeriod
    launchPeriod = await context.polkadotApi.consts.democracy.launchPeriod;

    // notePreimage
    encodedHash = await notePreimagePrecompile(
      context,
      iFace,
      context.polkadotApi.tx.parachainStaking.setParachainBondAccount(GENESIS_ACCOUNT)
    );

    // propose
    await sendPrecompileTx(
      context,
      ADDRESS_DEMO_PRECOMPILE,
      SELECTORS,
      GENESIS_ACCOUNT,
      GENESIS_ACCOUNT_PRIVATE_KEY,
      "propose",
      [encodedHash, numberToHex(Number(PROPOSAL_AMOUNT))]
    );

    // second
    await sendPrecompileTx(
      context,
      ADDRESS_DEMO_PRECOMPILE,
      SELECTORS,
      ALITH,
      ALITH_PRIV_KEY,
      "second",
      [numberToHex(0), numberToHex(1000)]
    );
  });
  // TODO: test getters
  it("second proposal", async function () {
    // publicProps
    const publicProps = await context.polkadotApi.query.democracy.publicProps();
    // encodedHash
    expect((publicProps.toHuman() as any)[0][1]).to.equal(encodedHash);
    // prop author
    expect((publicProps.toHuman() as any)[0][2]).to.equal(GENESIS_ACCOUNT);

    // depositOf
    const depositOf = await context.polkadotApi.query.democracy.depositOf(0);
    expect((depositOf.toHuman() as any)[1]).to.equal("1,000,000,000,000,000,000,000");
    expect((depositOf.toHuman() as any)[0][1]).to.equal(ALITH);
  });
  it("check launch period", async function () {
    // launchPeriod
    expect(launchPeriod.toHuman()).to.equal("7,200");
  });
  it("check referendum is up", async function () {
    this.timeout(1000000);
    // let Launchperiod elapse to turn the proposal into a referendum
    // launchPeriod minus the 3 blocks that already elapsed
    for (let i = 0; i < Number(launchPeriod) - 3; i++) {
      await context.createBlock();
    }
    // referendumCount
    let referendumCount = await context.polkadotApi.query.democracy.referendumCount();
    expect(referendumCount.toHuman()).to.equal("1");

    // publicPropCount
    const publicPropCount = await context.polkadotApi.query.democracy.publicPropCount();
    expect(publicPropCount.toHuman()).to.equal("1");

    // referendumInfoOf
    const referendumInfoOf = await context.polkadotApi.query.democracy.referendumInfoOf(0);
    expect((referendumInfoOf.toHuman() as any).Ongoing.proposalHash).to.equal(encodedHash);
  });
});
Example #29
Source File: nomination.test.ts    From interbtc-api with Apache License 2.0 4 votes vote down vote up
// TODO: readd this once we want to activate nomination
describe.skip("NominationAPI", () => {
    let api: ApiPromise;
    let userInterBtcAPI: InterBtcApi;
    let sudoInterBtcAPI: InterBtcApi;
    let sudoAccount: KeyringPair;
    let userAccount: KeyringPair;
    let vault_1: KeyringPair;
    let vault_1_id: InterbtcPrimitivesVaultId;
    let bitcoinCoreClient: BitcoinCoreClient;

    let wrappedCurrency: WrappedCurrency;
    let collateralCurrency: CollateralCurrency;

    before(async () => {
        api = await createSubstrateAPI(PARACHAIN_ENDPOINT);
        const keyring = new Keyring({ type: "sr25519" });
        sudoAccount = keyring.addFromUri(SUDO_URI);
        userAccount = keyring.addFromUri(USER_1_URI);
        // TODO: remove all uses of config currencies and query the chain instead
        userInterBtcAPI = new DefaultInterBtcApi(api, "regtest", userAccount, ESPLORA_BASE_PATH);
        sudoInterBtcAPI = new DefaultInterBtcApi(api, "regtest", sudoAccount, ESPLORA_BASE_PATH);
        collateralCurrency = getCorrespondingCollateralCurrency(userInterBtcAPI.getGovernanceCurrency());
        wrappedCurrency = userInterBtcAPI.getWrappedCurrency();
        vault_1 = keyring.addFromUri(VAULT_1_URI);
        vault_1_id = newVaultId(api, vault_1.address, collateralCurrency, wrappedCurrency);

        if (!(await sudoInterBtcAPI.nomination.isNominationEnabled())) {
            console.log("Enabling nomination...");
            await sudo(sudoInterBtcAPI, () => sudoInterBtcAPI.nomination.setNominationEnabled(true));
        }

        // The account of a vault from docker-compose
        vault_1 = keyring.addFromUri(VAULT_1_URI);
        bitcoinCoreClient = new BitcoinCoreClient(
            BITCOIN_CORE_NETWORK,
            BITCOIN_CORE_HOST,
            BITCOIN_CORE_USERNAME,
            BITCOIN_CORE_PASSWORD,
            BITCOIN_CORE_PORT,
            BITCOIN_CORE_WALLET
        );
    });

    after(() => {
        return api.disconnect();
    });

    it("Should opt a vault in and out of nomination", async () => {
        await optInWithAccount(vault_1, currencyIdToMonetaryCurrency(vault_1_id.currencies.collateral) as CollateralCurrency);
        const nominationVaults = await userInterBtcAPI.nomination.listVaults();
        assert.equal(1, nominationVaults.length);
        assert.equal(vault_1.address, nominationVaults.map(v => v.accountId.toString())[0]);
        await optOutWithAccount(vault_1, currencyIdToMonetaryCurrency(vault_1_id.currencies.collateral) as CollateralCurrency);
        assert.equal(0, (await userInterBtcAPI.nomination.listVaults()).length);
    }).timeout(60000);

    async function setIssueFee(x: BN) {
        await setNumericStorage(api, "Fee", "IssueFee", x, sudoAccount, 128);
    }

    it("Should nominate to and withdraw from a vault", async () => {
        await optInWithAccount(vault_1, currencyIdToMonetaryCurrency(vault_1_id.currencies.collateral) as CollateralCurrency);
        const issueFee = await userInterBtcAPI.fee.getIssueFee();
        const collateralCurrencyIdLiteral = currencyIdToLiteral(vault_1_id.currencies.collateral) as CollateralIdLiteral;
        const vault = await userInterBtcAPI.vaults.get(vault_1_id.accountId, collateralCurrencyIdLiteral);
        const collateralCurrency = currencyIdToMonetaryCurrency(vault.id.currencies.collateral) as Currency<CollateralUnit>;
        const nominatorDeposit = newMonetaryAmount(1, collateralCurrency, true);
        try {
            // Set issue fees to 100%
            await setIssueFee(new BN("1000000000000000000"));
            const stakingCapacityBeforeNomination = await userInterBtcAPI.vaults.getStakingCapacity(
                vault_1_id.accountId,
                collateralCurrencyIdLiteral
            );
            // Deposit
            await userInterBtcAPI.nomination.depositCollateral(vault_1_id.accountId, nominatorDeposit);
            const stakingCapacityAfterNomination = await userInterBtcAPI.vaults.getStakingCapacity(
                vault_1_id.accountId,
                collateralCurrencyIdLiteral
            );
            assert.equal(
                stakingCapacityBeforeNomination.sub(nominatorDeposit).toString(),
                stakingCapacityAfterNomination.toString(),
                "Nomination failed to decrease staking capacity"
            );
            const nominationPairs = await userInterBtcAPI.nomination.list();
            assert.equal(2, nominationPairs.length, "There should be one nomination pair in the system, besides the vault to itself");

            const userAddress = userAccount.address;
            const vault_1Address = vault_1.address;

            const nomination = nominationPairs.find((nomination) => userAddress == nomination.nominatorId.toString()) as Nomination;

            assert.equal(userAddress, nomination.nominatorId.toString());
            assert.equal(vault_1Address, nomination.vaultId.accountId.toString());

            const amountToIssue = newMonetaryAmount(0.00001, wrappedCurrency, true);
            await issueSingle(userInterBtcAPI, bitcoinCoreClient, userAccount, amountToIssue, vault_1_id);
            const wrappedRewardsBeforeWithdrawal = (
                await userInterBtcAPI.nomination.getNominatorReward(
                    vault_1_id.accountId,
                    collateralCurrencyIdLiteral,
                    tickerToCurrencyIdLiteral(wrappedCurrency.ticker),
                    newAccountId(api, userAccount.address),
                )
            ).toBig();
            assert.isTrue(
                wrappedRewardsBeforeWithdrawal.gt(0),
                "Nominator should receive non-zero wrapped tokens"
            );

            // Withdraw Rewards
            await userInterBtcAPI.rewards.withdrawRewards(vault_1_id);
            // Withdraw Collateral
            await userInterBtcAPI.nomination.withdrawCollateral(vault_1_id.accountId, nominatorDeposit);

            const nominatorsAfterWithdrawal = await userInterBtcAPI.nomination.list();
            // The vault always has a "nomination" to itself
            assert.equal(1, nominatorsAfterWithdrawal.length);
            const totalNomination = await userInterBtcAPI.nomination.getTotalNomination(
                newAccountId(api, userAccount.address),
                currencyIdToMonetaryCurrency(vault_1_id.currencies.collateral) as CollateralCurrency,
            );
            assert.equal(totalNomination.toString(), "0");
        } catch(error) {
            throw error;
        } finally {
            await setIssueFee(encodeUnsignedFixedPoint(api, issueFee));
            await optOutWithAccount(vault_1, currencyIdToMonetaryCurrency(vault_1_id.currencies.collateral) as CollateralCurrency);
        }
    }).timeout(5 * 60000);

    async function optInWithAccount(vaultAccount: KeyringPair, collateralCurrency: CollateralCurrency) {
        // will fail if vault is already opted in
        await callWith(userInterBtcAPI, vaultAccount, () => userInterBtcAPI.nomination.optIn(collateralCurrency));
    }

    async function optOutWithAccount(vaultAccount: KeyringPair, collateralCurrency: CollateralCurrency) {
        await callWith(userInterBtcAPI, vaultAccount, () => userInterBtcAPI.nomination.optOut(collateralCurrency));
    }
});