From 7761f2e6be0d62ca9ac690b3e5295d8aa2b6c7f0 Mon Sep 17 00:00:00 2001 From: Robert Leonard <40375385+Robert-H-Leonard@users.noreply.github.com> Date: Tue, 16 Jul 2024 05:10:14 -0700 Subject: [PATCH] Add cli commands to allow gatekeepers to manage tokens (#59) * add command to allow gatekeeper to issue tokens on a given network * Command to verify an address on a network * Make input to refresh partial * Refresh token cli command * Add freeze and unfreeze commands * add cli call to fetch token * Added command to fetch token data * Add integration test for a gatekeeper issuing a token via cli * Generate did if not initialized * Final test for token operations --------- Robert Leonard --- gatekeeper-cli/src/commands/fetch-token-id.ts | 58 +++++ .../src/commands/fetch-token-on-network.ts | 58 +++++ gatekeeper-cli/src/commands/freeze-token.ts | 58 +++++ gatekeeper-cli/src/commands/issue-token.ts | 78 +++++++ gatekeeper-cli/src/commands/refresh-token.ts | 72 ++++++ gatekeeper-cli/src/commands/revoke-token.ts | 58 +++++ gatekeeper-cli/src/commands/unfreeze-token.ts | 65 ++++++ .../src/commands/upload-gatekeeper-config.ts | 4 +- gatekeeper-cli/src/commands/verify-address.ts | 56 +++++ .../test/integration/token-operation.spec.ts | 211 ++++++++++++++++++ gatekeeper-cli/src/utils/index.ts | 1 + .../src/service/GatewayTsInternal.ts | 2 +- 12 files changed, 718 insertions(+), 3 deletions(-) create mode 100644 gatekeeper-cli/src/commands/fetch-token-id.ts create mode 100644 gatekeeper-cli/src/commands/fetch-token-on-network.ts create mode 100644 gatekeeper-cli/src/commands/freeze-token.ts create mode 100644 gatekeeper-cli/src/commands/issue-token.ts create mode 100644 gatekeeper-cli/src/commands/refresh-token.ts create mode 100644 gatekeeper-cli/src/commands/revoke-token.ts create mode 100644 gatekeeper-cli/src/commands/unfreeze-token.ts create mode 100644 gatekeeper-cli/src/commands/verify-address.ts create mode 100644 gatekeeper-cli/src/test/integration/token-operation.spec.ts diff --git a/gatekeeper-cli/src/commands/fetch-token-id.ts b/gatekeeper-cli/src/commands/fetch-token-id.ts new file mode 100644 index 00000000..2b9e316a --- /dev/null +++ b/gatekeeper-cli/src/commands/fetch-token-id.ts @@ -0,0 +1,58 @@ +import { + confirmationsFlag, + feesFlag, gatekeeperNetworkFlag, + gatewayTokenAddressFlag, + chainFlag, parseFlagsWithPrivateKey, + privateKeyFlag, gasLimitFlag, + gatewayNetworkAddressFlag, + } from '../utils/oclif/flags' + import {Args, Command, Flags} from '@oclif/core' + import {makeGatewayNetworkTs, makeGatewayTs} from '../utils/oclif/utils' + import { utils } from 'ethers'; + + export default class FetchTokenId extends Command { + static description = 'Fetch the token id for a given address on a given gateway network'; + + static examples = [ + `$ gateway-eth freeze-token 0x893F4Be53274353CD3379C87C8fd1cb4f8458F94 -n 123 + `, + ]; + + static flags = { + help: Flags.help({char: 'h'}), + privateKey: privateKeyFlag(), + gatewayTokenAddress: gatewayTokenAddressFlag(), + gatekeeperNetwork: gatekeeperNetworkFlag(), + gatewayNetworkAddress: gatewayNetworkAddressFlag(), + chain: chainFlag(), + fees: feesFlag(), + gasLimit: gasLimitFlag(), + confirmations: confirmationsFlag(), + }; + + static args = { + tokenOwner: Args.string({name: 'tokenOwner', required: true, description: 'Owner of the specified gateway token'}), + networkName: Args.string({name: 'networkName', required: true, description: 'Name of the network'}), + onlyActive: Args.boolean({name: 'onlyActive', required: false, description: "Flag to indicate only fetching an active token id"}) + } + + async run(): Promise { + const {args, flags} = await this.parse(FetchTokenId) + + const tokenOwner: string = args.tokenOwner + const parsedFlags = parseFlagsWithPrivateKey(flags) + + + const gatewayNetwork = await makeGatewayNetworkTs(parsedFlags) + const gatewayTokenTs = await makeGatewayTs(parsedFlags) + + const networkId = await gatewayNetwork.getNetworkId(args.networkName); + const tokenId = await gatewayTokenTs.getTokenId(tokenOwner, networkId.valueOf() as bigint, args.onlyActive); + + + this.log( + `Fetched token id ${tokenId}` + ) + } + } + \ No newline at end of file diff --git a/gatekeeper-cli/src/commands/fetch-token-on-network.ts b/gatekeeper-cli/src/commands/fetch-token-on-network.ts new file mode 100644 index 00000000..f34f8f58 --- /dev/null +++ b/gatekeeper-cli/src/commands/fetch-token-on-network.ts @@ -0,0 +1,58 @@ +import { + confirmationsFlag, + feesFlag, gatekeeperNetworkFlag, + gatewayTokenAddressFlag, + chainFlag, parseFlagsWithPrivateKey, + privateKeyFlag, gasLimitFlag, + gatewayNetworkAddressFlag, + } from '../utils/oclif/flags' + import {Args, Command, Flags} from '@oclif/core' + import {makeGatewayNetworkTs, makeGatewayTs} from '../utils/oclif/utils' + import { utils } from 'ethers'; + + export default class FetchTokenId extends Command { + static description = 'Fetch the token id for a given address on a given gateway network'; + + static examples = [ + `$ gateway-eth freeze-token 0x893F4Be53274353CD3379C87C8fd1cb4f8458F94 -n 123 + `, + ]; + + static flags = { + help: Flags.help({char: 'h'}), + privateKey: privateKeyFlag(), + gatewayTokenAddress: gatewayTokenAddressFlag(), + gatekeeperNetwork: gatekeeperNetworkFlag(), + gatewayNetworkAddress: gatewayNetworkAddressFlag(), + chain: chainFlag(), + fees: feesFlag(), + gasLimit: gasLimitFlag(), + confirmations: confirmationsFlag(), + }; + + static args = { + tokenOwner: Args.string({name: 'tokenOwner', required: true, description: 'Owner of the specified gateway token'}), + networkName: Args.string({name: 'networkName', required: true, description: 'Name of the network'}), + onlyActive: Args.boolean({name: 'onlyActive', required: false, description: "Flag to indicate only fetching an active token id"}) + } + + async run(): Promise { + const {args, flags} = await this.parse(FetchTokenId) + + const tokenOwner: string = args.tokenOwner + const parsedFlags = parseFlagsWithPrivateKey(flags) + + + const gatewayNetwork = await makeGatewayNetworkTs(parsedFlags) + const gatewayTokenTs = await makeGatewayTs(parsedFlags) + + const networkId = await gatewayNetwork.getNetworkId(utils.formatBytes32String(args.networkName)); + const tokenData = await gatewayTokenTs.getFirstTokenOnNetwork(tokenOwner, networkId.valueOf() as bigint, args.onlyActive); + + + this.log( + `Fetched token ${JSON.stringify(tokenData)}` + ) + } + } + \ No newline at end of file diff --git a/gatekeeper-cli/src/commands/freeze-token.ts b/gatekeeper-cli/src/commands/freeze-token.ts new file mode 100644 index 00000000..a797acb5 --- /dev/null +++ b/gatekeeper-cli/src/commands/freeze-token.ts @@ -0,0 +1,58 @@ +import { + confirmationsFlag, + feesFlag, gatekeeperNetworkFlag, + gatewayTokenAddressFlag, + chainFlag, parseFlagsWithPrivateKey, + privateKeyFlag, gasLimitFlag, + gatewayNetworkAddressFlag, + } from '../utils/oclif/flags' + import {Args, Command, Flags} from '@oclif/core' + import {makeGatewayNetworkTs, makeGatewayTs} from '../utils/oclif/utils' + import { utils, Wallet } from 'ethers'; + + export default class FreezeToken extends Command { + static description = 'Gatekeeper can freeze tokens they issued'; + + static examples = [ + `$ gateway-eth freeze-token 0x893F4Be53274353CD3379C87C8fd1cb4f8458F94 -n 123 + `, + ]; + + static flags = { + help: Flags.help({char: 'h'}), + privateKey: privateKeyFlag(), + gatewayTokenAddress: gatewayTokenAddressFlag(), + gatekeeperNetwork: gatekeeperNetworkFlag(), + gatewayNetworkAddress: gatewayNetworkAddressFlag(), + chain: chainFlag(), + fees: feesFlag(), + gasLimit: gasLimitFlag(), + confirmations: confirmationsFlag(), + }; + + static args = { + tokenOwner: Args.string({name: 'tokenOwner', required: true, description: 'Owner of the specified gateway token'}), + networkName: Args.string({name: 'networkName', required: true, description: 'Name of the network'}), + } + + async run(): Promise { + const {args, flags} = await this.parse(FreezeToken) + + const tokenOwner: string = args.tokenOwner + const parsedFlags = parseFlagsWithPrivateKey(flags) + + + const gatewayNetwork = await makeGatewayNetworkTs(parsedFlags) + const gatewayTokenTs = await makeGatewayTs(parsedFlags) + + const networkId = await gatewayNetwork.getNetworkId(args.networkName); + const sendableTransaction = await gatewayTokenTs.freeze(tokenOwner, networkId.valueOf() as bigint) + + const receipt = await sendableTransaction.wait(flags.confirmations) + + this.log( + `Froze gateway token. TxHash: ${receipt.transactionHash}`, + ) + } + } + \ No newline at end of file diff --git a/gatekeeper-cli/src/commands/issue-token.ts b/gatekeeper-cli/src/commands/issue-token.ts new file mode 100644 index 00000000..c98dc9f3 --- /dev/null +++ b/gatekeeper-cli/src/commands/issue-token.ts @@ -0,0 +1,78 @@ +import { + confirmationsFlag, + feesFlag, gatekeeperNetworkFlag, + gatewayTokenAddressFlag, + chainFlag, parseFlagsWithPrivateKey, + privateKeyFlag, gasLimitFlag, + gatewayNetworkAddressFlag, + } from '../utils/oclif/flags' + import {Args, Command, Flags} from '@oclif/core' + import {makeGatewayNetworkTs, makeGatewayTs} from '../utils/oclif/utils' + import { utils, Wallet } from 'ethers'; + + export default class IssueToken extends Command { + static description = 'Gatekeepers can issue tokens on their respective networks'; + + static examples = [ + `$ gateway-eth issue-token 0x893F4Be53274353CD3379C87C8fd1cb4f8458F94 -n 123 + `, + ]; + + static flags = { + help: Flags.help({char: 'h'}), + privateKey: privateKeyFlag(), + gatewayTokenAddress: gatewayTokenAddressFlag(), + gatekeeperNetwork: gatekeeperNetworkFlag(), + gatewayNetworkAddress: gatewayNetworkAddressFlag(), + chain: chainFlag(), + fees: feesFlag(), + gasLimit: gasLimitFlag(), + confirmations: confirmationsFlag(), + }; + + static args = { + tokenOwner: Args.string({name: 'tokenOwner', required: true, description: 'Owner of the specified gateway token'}), + networkName: Args.string({name: 'networkName', required: true, description: 'Name of the network'}), + expirationTimeInSeconds: Args.integer({name: 'expirationTimeInSeconds', required: true, description: "The amount of time in seconds before a token expires. Default value is set to 0. Network primary authority can override this value"}), + feeSender: Args.string({name: 'feeSender', required: true, description: "Address that will be paying the fee"}), + chargeType: Args.string({name: "chargeType", required: true, description: "Supproted types are: NONE, ETH and ERC20. This is set by the network"}), + feeValue: Args.integer({name: "feeValue", required: true, description: "Value charged to fee sender. They must approve before hand"}) + } + + async run(): Promise { + const {args, flags} = await this.parse(IssueToken) + + const tokenOwner: string = args.tokenOwner + const parsedFlags = parseFlagsWithPrivateKey(flags) + + this.log(`Issuing token for address: ${tokenOwner} on network ${args.networkName}`) + + + + const gatewayNetwork = await makeGatewayNetworkTs(parsedFlags) + const gatewayTokenTs = await makeGatewayTs(parsedFlags) + + const networkId = await gatewayNetwork.getNetworkId(args.networkName); + const feeRecipient = (new Wallet(parsedFlags.privateKey)).address + + this.log(`Issuing token for address: ${tokenOwner} on network ${args.networkName}`) + const sendableTransaction = await gatewayTokenTs.issue( + tokenOwner, + networkId.valueOf() as bigint, + args.expirationTimeInSeconds, + 0, + {feeRecipient , feeSender: args.feeSender}, + { + tokenSender: args.feeSender, + recipient: feeRecipient + } + ) + + const receipt = await sendableTransaction.wait(flags.confirmations) + + this.log( + `Issued gateway token to use. TxHash: ${receipt.transactionHash}`, + ) + } + } + \ No newline at end of file diff --git a/gatekeeper-cli/src/commands/refresh-token.ts b/gatekeeper-cli/src/commands/refresh-token.ts new file mode 100644 index 00000000..18df92b8 --- /dev/null +++ b/gatekeeper-cli/src/commands/refresh-token.ts @@ -0,0 +1,72 @@ +import { + confirmationsFlag, + feesFlag, gatekeeperNetworkFlag, + gatewayTokenAddressFlag, + chainFlag, parseFlagsWithPrivateKey, + privateKeyFlag, gasLimitFlag, + gatewayNetworkAddressFlag, + } from '../utils/oclif/flags' + import {Args, Command, Flags} from '@oclif/core' + import {makeGatewayNetworkTs, makeGatewayTs} from '../utils/oclif/utils' + import { utils, Wallet } from 'ethers'; + + export default class RefreshToken extends Command { + static description = 'Gatekeepers can refresh tokens (such as setting a new expire time) on their respective networks'; + + static examples = [ + `$ gateway-eth issue-token 0x893F4Be53274353CD3379C87C8fd1cb4f8458F94 -n 123 + `, + ]; + + static flags = { + help: Flags.help({char: 'h'}), + privateKey: privateKeyFlag(), + gatewayTokenAddress: gatewayTokenAddressFlag(), + gatekeeperNetwork: gatekeeperNetworkFlag(), + gatewayNetworkAddress: gatewayNetworkAddressFlag(), + chain: chainFlag(), + fees: feesFlag(), + gasLimit: gasLimitFlag(), + confirmations: confirmationsFlag(), + }; + + static args = { + tokenOwner: Args.string({name: 'tokenOwner', required: true, description: 'Owner of the specified gateway token'}), + networkName: Args.string({name: 'networkName', required: true, description: 'Name of the network'}), + expirationTimeInSeconds: Args.integer({name: 'expirationTimeInSeconds', required: true, description: "The amount of time in seconds before a token expires. Default value is set to 0. Network primary authority can override this value"}), + feeSender: Args.string({name: 'feeSender', required: true, description: "Address that will be paying the fee"}), + chargeType: Args.string({name: "chargeType", required: true, description: "Supproted types are: NONE, ETH and ERC20. This is set by the network"}), + feeValue: Args.integer({name: "feeValue", required: true, description: "Value charged to fee sender. They must approve before hand"}) + } + + async run(): Promise { + const {args, flags} = await this.parse(RefreshToken) + + const tokenOwner: string = args.tokenOwner + const parsedFlags = parseFlagsWithPrivateKey(flags) + + + const gatewayNetwork = await makeGatewayNetworkTs(parsedFlags) + const gatewayTokenTs = await makeGatewayTs(parsedFlags) + + const networkId = await gatewayNetwork.getNetworkId(args.networkName); + const feeRecipient = (new Wallet(parsedFlags.privateKey)).address + const sendableTransaction = await gatewayTokenTs.refresh( + tokenOwner, + networkId.valueOf() as bigint, + {feeRecipient , feeSender: args.feeSender}, + args.expirationTimeInSeconds, + { + tokenSender: args.feeSender, + recipient: feeRecipient + } + ) + + const receipt = await sendableTransaction.wait(flags.confirmations) + + this.log( + `Refreshed gateway token. TxHash: ${receipt.transactionHash}`, + ) + } + } + \ No newline at end of file diff --git a/gatekeeper-cli/src/commands/revoke-token.ts b/gatekeeper-cli/src/commands/revoke-token.ts new file mode 100644 index 00000000..929e67ee --- /dev/null +++ b/gatekeeper-cli/src/commands/revoke-token.ts @@ -0,0 +1,58 @@ +import { + confirmationsFlag, + feesFlag, gatekeeperNetworkFlag, + gatewayTokenAddressFlag, + chainFlag, parseFlagsWithPrivateKey, + privateKeyFlag, gasLimitFlag, + gatewayNetworkAddressFlag, + } from '../utils/oclif/flags' + import {Args, Command, Flags} from '@oclif/core' + import {makeGatewayNetworkTs, makeGatewayTs} from '../utils/oclif/utils' + import { utils, Wallet } from 'ethers'; + + export default class RevokeToken extends Command { + static description = 'Gatekeeper can revoke tokens they issued'; + + static examples = [ + `$ gateway-eth revoke-token 0x893F4Be53274353CD3379C87C8fd1cb4f8458F94 -n 123 + `, + ]; + + static flags = { + help: Flags.help({char: 'h'}), + privateKey: privateKeyFlag(), + gatewayTokenAddress: gatewayTokenAddressFlag(), + gatekeeperNetwork: gatekeeperNetworkFlag(), + gatewayNetworkAddress: gatewayNetworkAddressFlag(), + chain: chainFlag(), + fees: feesFlag(), + gasLimit: gasLimitFlag(), + confirmations: confirmationsFlag(), + }; + + static args = { + tokenOwner: Args.string({name: 'tokenOwner', required: true, description: 'Owner of the specified gateway token'}), + networkName: Args.string({name: 'networkName', required: true, description: 'Name of the network'}), + } + + async run(): Promise { + const {args, flags} = await this.parse(RevokeToken) + + const tokenOwner: string = args.tokenOwner + const parsedFlags = parseFlagsWithPrivateKey(flags) + + + const gatewayNetwork = await makeGatewayNetworkTs(parsedFlags) + const gatewayTokenTs = await makeGatewayTs(parsedFlags) + + const networkId = await gatewayNetwork.getNetworkId(args.networkName); + const sendableTransaction = await gatewayTokenTs.revoke(tokenOwner, networkId.valueOf() as bigint) + + const receipt = await sendableTransaction.wait(flags.confirmations) + + this.log( + `Revoked gateway token. TxHash: ${receipt.transactionHash}`, + ) + } + } + \ No newline at end of file diff --git a/gatekeeper-cli/src/commands/unfreeze-token.ts b/gatekeeper-cli/src/commands/unfreeze-token.ts new file mode 100644 index 00000000..96a1bbbc --- /dev/null +++ b/gatekeeper-cli/src/commands/unfreeze-token.ts @@ -0,0 +1,65 @@ +import { + confirmationsFlag, + feesFlag, gatekeeperNetworkFlag, + gatewayTokenAddressFlag, + chainFlag, parseFlagsWithPrivateKey, + privateKeyFlag, gasLimitFlag, + gatewayNetworkAddressFlag, + } from '../utils/oclif/flags' + import {Args, Command, Flags} from '@oclif/core' + import {makeGatewayNetworkTs, makeGatewayTs} from '../utils/oclif/utils' + import { utils, Wallet } from 'ethers'; + + export default class UnfreezeToken extends Command { + static description = 'Gatekeeper can unfreeze tokens they issued'; + + static examples = [ + `$ gateway-eth unfreeze-token 0x893F4Be53274353CD3379C87C8fd1cb4f8458F94 -n 123 + `, + ]; + + static flags = { + help: Flags.help({char: 'h'}), + privateKey: privateKeyFlag(), + gatewayTokenAddress: gatewayTokenAddressFlag(), + gatekeeperNetwork: gatekeeperNetworkFlag(), + gatewayNetworkAddress: gatewayNetworkAddressFlag(), + chain: chainFlag(), + fees: feesFlag(), + gasLimit: gasLimitFlag(), + confirmations: confirmationsFlag(), + }; + + static args = { + tokenOwner: Args.string({name: 'tokenOwner', required: true, description: 'Owner of the specified gateway token'}), + networkName: Args.string({name: 'networkName', required: true, description: 'Name of the network'}), + feeSender: Args.string({name: 'feeSender', required: true, description: "Address that will be paying the fee"}), + } + + async run(): Promise { + const {args, flags} = await this.parse(UnfreezeToken) + + const tokenOwner: string = args.tokenOwner + const parsedFlags = parseFlagsWithPrivateKey(flags) + + + const gatewayNetwork = await makeGatewayNetworkTs(parsedFlags) + const gatewayTokenTs = await makeGatewayTs(parsedFlags) + + const networkId = await gatewayNetwork.getNetworkId(args.networkName); + const feeRecipient = (new Wallet(parsedFlags.privateKey)).address + + const sendableTransaction = await gatewayTokenTs.unfreeze( + tokenOwner, + networkId.valueOf() as bigint, + {feeRecipient , feeSender: args.feeSender} + ) + + const receipt = await sendableTransaction.wait(flags.confirmations) + + this.log( + `Unfroze gateway token. TxHash: ${receipt.transactionHash}`, + ) + } + } + \ No newline at end of file diff --git a/gatekeeper-cli/src/commands/upload-gatekeeper-config.ts b/gatekeeper-cli/src/commands/upload-gatekeeper-config.ts index 57064d95..d1031838 100644 --- a/gatekeeper-cli/src/commands/upload-gatekeeper-config.ts +++ b/gatekeeper-cli/src/commands/upload-gatekeeper-config.ts @@ -44,9 +44,9 @@ import { const didRegistry = await makeDidRegistryClient(args.didRegistryContractAddress, parsedFlags.privateKey, parsedFlags.provider); // Check is did already initialized - const isDidInitialized = didRegistry.isGenerativeDidState(didRegistry.getDid()) + const isDidGenerative = await didRegistry.isGenerativeDidState(didRegistry.getDid()) - if(!isDidInitialized) { + if(isDidGenerative) { this.log(` Initializing did state`) const initilizeTx = await didRegistry.initializeDidState(); const initilizeReceipt = await initilizeTx.wait(flags.confirmations); diff --git a/gatekeeper-cli/src/commands/verify-address.ts b/gatekeeper-cli/src/commands/verify-address.ts new file mode 100644 index 00000000..4f8742b0 --- /dev/null +++ b/gatekeeper-cli/src/commands/verify-address.ts @@ -0,0 +1,56 @@ +import { + confirmationsFlag, + feesFlag, gatekeeperNetworkFlag, + gatewayTokenAddressFlag, + chainFlag, parseFlagsWithPrivateKey, + privateKeyFlag, gasLimitFlag, + gatewayNetworkAddressFlag, + } from '../utils/oclif/flags' + import {Args, Command, Flags} from '@oclif/core' + import {makeGatewayNetworkTs, makeGatewayTs} from '../utils/oclif/utils' + import { utils } from 'ethers'; + + export default class VerifyAddress extends Command { + static description = 'Checks if the given address has a valid gateway token on the given network'; + + static examples = [ + `$ gateway-eth verify-address 0x893F4Be53274353CD3379C87C8fd1cb4f8458F94 networkName + `, + ]; + + static flags = { + help: Flags.help({char: 'h'}), + privateKey: privateKeyFlag(), + gatewayTokenAddress: gatewayTokenAddressFlag(), + gatekeeperNetwork: gatekeeperNetworkFlag(), + gatewayNetworkAddress: gatewayNetworkAddressFlag(), + chain: chainFlag(), + fees: feesFlag(), + gasLimit: gasLimitFlag(), + confirmations: confirmationsFlag(), + }; + + static args = { + address: Args.string({name: 'address', required: true, description: 'Address to verify'}), + networkName: Args.string({name: 'networkName', required: true, description: 'Name of the network'}) + } + + async run(): Promise { + const {args, flags} = await this.parse(VerifyAddress) + + const parsedFlags = parseFlagsWithPrivateKey(flags) + + + const gatewayTokenTs = await makeGatewayTs(parsedFlags) + const gatewayNetwork = await makeGatewayNetworkTs(parsedFlags) + const networkId = await gatewayNetwork.getNetworkId(args.networkName); + + const hasValidToken = await gatewayTokenTs.verify(args.address, networkId.valueOf() as bigint); + + + this.log( + `Address ${args.address} ${hasValidToken ? "has" : "does not have"} a valid token`, + ) + } + } + \ No newline at end of file diff --git a/gatekeeper-cli/src/test/integration/token-operation.spec.ts b/gatekeeper-cli/src/test/integration/token-operation.spec.ts new file mode 100644 index 00000000..b7236397 --- /dev/null +++ b/gatekeeper-cli/src/test/integration/token-operation.spec.ts @@ -0,0 +1,211 @@ +import { BaseProvider } from "@ethersproject/providers"; +import * as dotenv from "dotenv"; +import { ethers, utils, Wallet } from "ethers"; +import {runCommand} from '@oclif/test' +import { BNB_TESTNET_CONTRACT_ADDRESSES, createRandomString, gatekeeperIdentityNetwork, FOUNDRY_DEFAULT_WALLET_TWO, GatewayNetworkClient, RANDOM_NETWORK_NAME, RANDOM_WALLET } from "../../utils"; +import assert = require('assert'); +import { GatewayNetworkClass } from "@identity.com/gateway-evm-ts-client/dist/service/GatewayNetwork"; +import { GatewayTs } from "@identity.com/gateway-eth-ts"; + +dotenv.config(); + +describe("Command: Token operations", function () { + let provider: BaseProvider; + let issuerWallet: Wallet; + let tokenReceiver: Wallet; + let networkClient: GatewayNetworkClass; + let gatewayTokenClient: GatewayTs; + let networkName: string; + + before("Initialize wallet", async function () { + this.timeout(20000); + provider = new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545"); // Test run against local node forked + + issuerWallet = gatekeeperIdentityNetwork.connect(provider); + tokenReceiver = FOUNDRY_DEFAULT_WALLET_TWO.connect(provider); + networkClient = GatewayNetworkClient(issuerWallet, BNB_TESTNET_CONTRACT_ADDRESSES.gatewayNetwork) + gatewayTokenClient = new GatewayTs(issuerWallet, BNB_TESTNET_CONTRACT_ADDRESSES.gatewayToken) + networkName = createRandomString(4); + + await networkClient.createNetwork({ + primaryAuthority: issuerWallet.address, + name: utils.formatBytes32String(networkName), + passExpireDurationInSeconds: 100000, + networkFeatureMask: 0, + networkFee: { + issueFee: 0, + refreshFee: 0, + expireFee: 0, + freezeFee: 0 + }, + supportedToken: '0x0000000000000000000000000000000000000000', + gatekeepers: [], + lastFeeUpdateTimestamp: 0, + description: utils.formatBytes32String("created with cli") + }); + + await networkClient.addGatekeeper(utils.formatBytes32String(networkName), issuerWallet.address); + }); + + it("issue token to user", async function () { + + const networkId = await networkClient.getNetworkId(networkName); + await networkClient.getNetwork(networkId.toString()); + + const result = await runCommand([ + "issue-token", + tokenReceiver.address, + networkName, + "0", + tokenReceiver.address, + "NONE", + "0", + `--privateKey=${issuerWallet.privateKey}`, + `--gatewayNetworkAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayNetwork}`, + `--gatewayTokenAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayToken}`, + `--chain=localhost` + ]) + + + const wasTxConfirmed = result.stdout.includes("Issued gateway token to use. TxHash:") + assert.equal(wasTxConfirmed, true, "Transaction should be confirmed on node") + assert.equal(result.error, undefined, "No errors should occur when creating network") + + const answer = await gatewayTokenClient.verify(tokenReceiver.address, networkId.valueOf() as bigint) + assert.equal(answer, true, "tokenReceiver should have valid token") + }); + + it("fetch token id", async function () { + const networkId = await networkClient.getNetworkId(networkName); + await networkClient.getNetwork(networkId.toString()); + + const result = await runCommand([ + "fetch-token-id", + tokenReceiver.address, + networkName, + "true", + `--privateKey=${issuerWallet.privateKey}`, + `--gatewayNetworkAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayNetwork}`, + `--gatewayTokenAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayToken}`, + `--chain=localhost` + ]) + + + const wasTxConfirmed = result.stdout.includes("Fetched token") + assert.equal(wasTxConfirmed, true, "Transaction should be confirmed on node") + assert.equal(result.error, undefined, "No errors should occur when creating network") + }); + + it("verify token", async function () { + const networkId = await networkClient.getNetworkId(networkName); + await networkClient.getNetwork(networkId.toString()); + + const result = await runCommand([ + "verify-address", + tokenReceiver.address, + networkName, + `--privateKey=${issuerWallet.privateKey}`, + `--gatewayNetworkAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayNetwork}`, + `--gatewayTokenAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayToken}`, + `--chain=localhost` + ]) + + + const wasTxConfirmed = result.stdout.includes("has a valid token") + assert.equal(wasTxConfirmed, true, "Transaction should be confirmed on node") + assert.equal(result.error, undefined, "No errors should occur when creating network") + }); + + it("freeze token", async function () { + const networkId = await networkClient.getNetworkId(networkName); + await networkClient.getNetwork(networkId.toString()); + + const result = await runCommand([ + "freeze-token", + tokenReceiver.address, + networkName, + `--privateKey=${issuerWallet.privateKey}`, + `--gatewayNetworkAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayNetwork}`, + `--gatewayTokenAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayToken}`, + `--chain=localhost` + ]) + + + const wasTxConfirmed = result.stdout.includes("Froze gateway token. TxHash:") + assert.equal(wasTxConfirmed, true, "Transaction should be confirmed on node") + assert.equal(result.error, undefined, "No errors should occur when creating network") + + const answer = await gatewayTokenClient.verify(tokenReceiver.address, networkId.valueOf() as bigint) + assert.equal(answer, false, "tokenReceiver should have invalid token") + }); + + it("unfreeze token", async function () { + const networkId = await networkClient.getNetworkId(networkName); + await networkClient.getNetwork(networkId.toString()); + + const result = await runCommand([ + "unfreeze-token", + tokenReceiver.address, + networkName, + tokenReceiver.address, + `--privateKey=${issuerWallet.privateKey}`, + `--gatewayNetworkAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayNetwork}`, + `--gatewayTokenAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayToken}`, + `--chain=localhost` + ]) + + + const wasTxConfirmed = result.stdout.includes("Unfroze gateway token. TxHash:") + assert.equal(wasTxConfirmed, true, "Transaction should be confirmed on node") + assert.equal(result.error, undefined, "No errors should occur when creating network") + + const answer = await gatewayTokenClient.verify(tokenReceiver.address, networkId.valueOf() as bigint) + assert.equal(answer, true, "tokenReceiver should have valid token again") + }); + + it("refresh token", async function () { + const networkId = await networkClient.getNetworkId(networkName); + await networkClient.getNetwork(networkId.toString()); + + const result = await runCommand([ + "refresh-token", + tokenReceiver.address, + networkName, + "100000000", + tokenReceiver.address, + "NONE", + "0", + `--privateKey=${issuerWallet.privateKey}`, + `--gatewayNetworkAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayNetwork}`, + `--gatewayTokenAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayToken}`, + `--chain=localhost` + ]) + + const wasTxConfirmed = result.stdout.includes("Refreshed gateway token. TxHash:") + assert.equal(wasTxConfirmed, true, "Transaction should be confirmed on node") + assert.equal(result.error, undefined, "No errors should occur when creating network") + }); + + it("revoke token", async function () { + const networkId = await networkClient.getNetworkId(networkName); + await networkClient.getNetwork(networkId.toString()); + + const result = await runCommand([ + "revoke-token", + tokenReceiver.address, + networkName, + `--privateKey=${issuerWallet.privateKey}`, + `--gatewayNetworkAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayNetwork}`, + `--gatewayTokenAddress=${BNB_TESTNET_CONTRACT_ADDRESSES.gatewayToken}`, + `--chain=localhost` + ]) + + + const wasTxConfirmed = result.stdout.includes("Revoked gateway token. TxHash:") + assert.equal(wasTxConfirmed, true, "Transaction should be confirmed on node") + assert.equal(result.error, undefined, "No errors should occur when creating network") + + const answer = await gatewayTokenClient.verify(tokenReceiver.address, networkId.valueOf() as bigint) + assert.equal(answer, false, "tokenReceiver should have invalid token") + }); +}) \ No newline at end of file diff --git a/gatekeeper-cli/src/utils/index.ts b/gatekeeper-cli/src/utils/index.ts index 721e5e0f..05efa56a 100644 --- a/gatekeeper-cli/src/utils/index.ts +++ b/gatekeeper-cli/src/utils/index.ts @@ -29,6 +29,7 @@ export const BNB_TESTNET_CONTRACT_ADDRESSES: GatewayProtocolContractAddresses = export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; export const FOUNDRY_DEFAULT_WALLET_ONE = new Wallet("0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d") // 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 export const FOUNDRY_DEFAULT_WALLET_TWO = new Wallet("0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"); // 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC +export const gatekeeperIdentityNetwork = new Wallet("0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba"); // 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc export const RANDOM_WALLET = Wallet.createRandom(); export const GATEKEEPER_ON_TESTNET = new Wallet("0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6") // 0xa0ee7a142d267c1f36714e4a8f75612f20a79720 diff --git a/gateway-eth-ts/src/service/GatewayTsInternal.ts b/gateway-eth-ts/src/service/GatewayTsInternal.ts index e59ef167..a1040024 100644 --- a/gateway-eth-ts/src/service/GatewayTsInternal.ts +++ b/gateway-eth-ts/src/service/GatewayTsInternal.ts @@ -135,7 +135,7 @@ export class GatewayTsInternal< network: bigint, partiesInCharge: ChargeParties, expiry?: number | BigNumber, - chargeType?: Charge + chargeType?: Partial ): Promise { const tokenId = await this.getTokenId(owner, network); const expirationTime = getExpirationTime(expiry);