From 1147b83f8c5a907e63cf4716c32a5bb541a338e7 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Wed, 25 Sep 2024 13:21:04 +0000 Subject: [PATCH] Add methods for creating auctions and add auction tests (#28) Part of [Service provider auctions](https://www.notion.so/Service-provider-auctions-a7b63697d818479493ec145ea6ea3c1c) Co-authored-by: IshaVenikar Co-authored-by: Isha Venikar Reviewed-on: https://git.vdb.to/cerc-io/registry-sdk/pulls/28 Co-authored-by: Prathamesh Musale Co-committed-by: Prathamesh Musale --- .gitea/workflows/test.yml | 6 +- README.md | 2 +- package.json | 4 +- proto/cerc/auction/module/v1/module.proto | 4 + proto/cerc/auction/v1/auction.proto | 93 ++-- proto/cerc/auction/v1/tx.proto | 82 +++- proto/cerc/bond/module/v1/module.proto | 4 + proto/cerc/bond/v1/tx.proto | 24 ++ proto/cerc/registry/module/v1/module.proto | 4 + proto/cerc/registry/v1/tx.proto | 23 + src/auction.test.ts | 392 ++++++++++++++--- src/authority-auction.test.ts | 139 ++++++ src/constants.ts | 3 + src/index.ts | 96 ++++- src/laconic-client.ts | 59 ++- src/proto/cerc/auction/module/v1/module.ts | 37 +- src/proto/cerc/auction/v1/auction.ts | 308 ++++++------- src/proto/cerc/auction/v1/tx.ts | 408 +++++++++++++++++- src/proto/cerc/bond/module/v1/module.ts | 37 +- src/proto/cerc/bond/v1/tx.ts | 150 +++++++ src/proto/cerc/registry/module/v1/module.ts | 37 +- src/proto/cerc/registry/v1/registry.ts | 14 +- src/proto/cerc/registry/v1/tx.ts | 155 ++++++- .../cosmos/base/query/v1beta1/pagination.ts | 14 +- src/proto/google/protobuf/descriptor.ts | 14 +- src/registry-client.ts | 11 +- src/testing/helper.ts | 6 +- src/types/cerc/auction/message.ts | 50 ++- 28 files changed, 1793 insertions(+), 383 deletions(-) create mode 100644 src/authority-auction.test.ts diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 032359e..9ded6b5 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -44,14 +44,14 @@ jobs: working-directory: laconicd/tests/sdk_tests run: ./run-tests.sh - - name: Start containers (auctions enabled) + - name: Start containers (authority auctions enabled) working-directory: laconicd/tests/sdk_tests env: TEST_AUCTION_ENABLED: true run: docker compose up -d - - name: Run auction tests + - name: Run authority auction tests working-directory: laconicd/tests/sdk_tests - run: ./run-tests.sh test:auctions + run: ./run-tests.sh test:authority-auctions - name: Start containers (expiry enabled) working-directory: laconicd/tests/sdk_tests diff --git a/README.md b/README.md index b63533f..59c8ca8 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Follow these steps to run the tests: - Run tests: ```bash - yarn test:auctions + yarn test:authority-auctions ``` - Run the tests for record and authority expiry diff --git a/package.json b/package.json index d74b0ad..f67f3d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/registry-sdk", - "version": "0.2.9", + "version": "0.2.10", "main": "dist/index.js", "types": "dist/index.d.ts", "repository": "git@github.com:cerc-io/registry-sdk.git", @@ -62,7 +62,7 @@ }, "scripts": { "test": "jest --runInBand --verbose --testPathPattern=src", - "test:auctions": "TEST_AUCTIONS_ENABLED=1 jest --runInBand --verbose src/auction.test.ts", + "test:authority-auctions": "TEST_AUCTIONS_ENABLED=1 jest --runInBand --verbose src/authority-auction.test.ts", "test:nameservice-expiry": "TEST_NAMESERVICE_EXPIRY=1 jest --runInBand --verbose src/nameservice-expiry.test.ts", "test:onboarding": "ONBOARDING_ENABLED=1 jest --runInBand --verbose src/onboarding.test.ts", "build": "tsc", diff --git a/proto/cerc/auction/module/v1/module.proto b/proto/cerc/auction/module/v1/module.proto index 287edff..c047198 100644 --- a/proto/cerc/auction/module/v1/module.proto +++ b/proto/cerc/auction/module/v1/module.proto @@ -10,4 +10,8 @@ message Module { option (cosmos.app.v1alpha1.module) = { go_import : "git.vdb.to/cerc-io/laconicd/x/auction" }; + + // authority defines the custom module authority. If not set, defaults to the + // governance module. + string authority = 2; } diff --git a/proto/cerc/auction/v1/auction.proto b/proto/cerc/auction/v1/auction.proto index f80c9d6..9198575 100644 --- a/proto/cerc/auction/v1/auction.proto +++ b/proto/cerc/auction/v1/auction.proto @@ -3,76 +3,44 @@ syntax = "proto3"; package cerc.auction.v1; import "gogoproto/gogo.proto"; -import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "cosmos/base/v1beta1/coin.proto"; option go_package = "git.vdb.to/cerc-io/laconicd/x/auction"; // Params defines the auction module parameters -message Params { - // Write custom stringer method - option (gogoproto.goproto_stringer) = false; - - // Duration of the commits phase in seconds - google.protobuf.Duration commits_duration = 1 [ - (gogoproto.nullable) = false, - (gogoproto.stdduration) = true, - (gogoproto.moretags) = "json:\"commits_duration\" yaml:\"commits_duration\"" - ]; - - // Duration of the reveals phase in seconds - google.protobuf.Duration reveals_duration = 2 [ - (gogoproto.nullable) = false, - (gogoproto.stdduration) = true, - (gogoproto.moretags) = "json:\"reveals_duration\" yaml:\"reveals_duration\"" - ]; - - // Commit fees - cosmos.base.v1beta1.Coin commit_fee = 3 [ - (gogoproto.nullable) = false, - (gogoproto.moretags) = "json:\"commit_fee\" yaml:\"commit_fee\"" - ]; - - // Reveal fees - cosmos.base.v1beta1.Coin reveal_fee = 4 [ - (gogoproto.nullable) = false, - (gogoproto.moretags) = "json:\"reveal_fee\" yaml:\"reveal_fee\"" - ]; - - // Minimum acceptable bid amount - cosmos.base.v1beta1.Coin minimum_bid = 5 [ - (gogoproto.nullable) = false, - (gogoproto.moretags) = "json:\"minimum_bid\" yaml:\"minimum_bid\"" - ]; -} +message Params {} // Auction represents a sealed-bid on-chain auction message Auction { option (gogoproto.goproto_getters) = false; string id = 1; - string status = 2; + + // Auction kind (vickrey | provider) + string kind = 2 [ (gogoproto.moretags) = "json:\"kind\" yaml:\"kind\"" ]; + + string status = 3; // Address of the creator of the auction - string owner_address = 3; + string owner_address = 4; // Timestamp at which the auction was created - google.protobuf.Timestamp create_time = 4 [ + google.protobuf.Timestamp create_time = 5 [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "json:\"create_time\" yaml:\"create_time\"" ]; // Timestamp at which the commits phase concluded - google.protobuf.Timestamp commits_end_time = 5 [ + google.protobuf.Timestamp commits_end_time = 6 [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "json:\"commits_end_time\" yaml:\"commits_end_time\"" ]; // Timestamp at which the reveals phase concluded - google.protobuf.Timestamp reveals_end_time = 6 [ + google.protobuf.Timestamp reveals_end_time = 7 [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "json:\"reveals_end_time\" yaml:\"reveals_end_time\"" @@ -80,35 +48,56 @@ message Auction { // Commit and reveal fees must both be paid when committing a bid // Reveal fee is returned only if the bid is revealed - cosmos.base.v1beta1.Coin commit_fee = 7 [ + cosmos.base.v1beta1.Coin commit_fee = 8 [ (gogoproto.nullable) = false, (gogoproto.moretags) = "json:\"commit_fee\" yaml:\"commit_fee\"" ]; - cosmos.base.v1beta1.Coin reveal_fee = 8 [ + cosmos.base.v1beta1.Coin reveal_fee = 9 [ (gogoproto.nullable) = false, (gogoproto.moretags) = "json:\"reveal_fee\" yaml:\"reveal_fee\"" ]; // Minimum acceptable bid amount for a valid commit - cosmos.base.v1beta1.Coin minimum_bid = 9 [ + // Only applicable in vickrey auctions + cosmos.base.v1beta1.Coin minimum_bid = 10 [ (gogoproto.nullable) = false, (gogoproto.moretags) = "json:\"minimum_bid\" yaml:\"minimum_bid\"" ]; - // Address of the winner - string winner_address = 10; + // Addresses of the winners + // (single winner for vickrey auction) + // (multiple winners for provider auctions) + repeated string winner_addresses = 11; - // Winning bid, i.e., the highest bid - cosmos.base.v1beta1.Coin winning_bid = 11 [ + // Winning bids, i.e. the best bids + repeated cosmos.base.v1beta1.Coin winning_bids = 12 [ (gogoproto.nullable) = false, - (gogoproto.moretags) = "json:\"winning_bid\" yaml:\"winning_bid\"" + (gogoproto.moretags) = "json:\"winning_bids\" yaml:\"winning_bids\"" ]; - // Amount the winner pays, i.e. the second highest auction - cosmos.base.v1beta1.Coin winning_price = 12 [ + // Auction winning price + // vickrey auction: second highest bid, paid by the winner + // provider auction: higest bid amongst winning_bids, paid by auction creator + // to each winner + cosmos.base.v1beta1.Coin winning_price = 13 [ (gogoproto.nullable) = false, (gogoproto.moretags) = "json:\"winning_price\" yaml:\"winning_price\"" ]; + + // Maximum acceptable bid amount for a valid commit + // Only applicable in provider auctions + cosmos.base.v1beta1.Coin max_price = 14 [ + (gogoproto.nullable) = false, + (gogoproto.moretags) = "json:\"max_price\" yaml:\"max_price\"" + ]; + + // Number of desired providers (num of auction winners) + // Only applicable in provider auctions + int32 num_providers = 15; + + bool funds_released = 16 [ + (gogoproto.moretags) = + "json:\"funds_released\" yaml:\"funds_released\"" ]; } // Auctions represent all the auctions in the module diff --git a/proto/cerc/auction/v1/tx.proto b/proto/cerc/auction/v1/tx.proto index b4b1795..c93e1be 100644 --- a/proto/cerc/auction/v1/tx.proto +++ b/proto/cerc/auction/v1/tx.proto @@ -7,6 +7,7 @@ import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "google/protobuf/duration.proto"; import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; import "cerc/auction/v1/auction.proto"; option go_package = "git.vdb.to/cerc-io/laconicd/x/auction"; @@ -29,6 +30,15 @@ service Msg { rpc RevealBid(MsgRevealBid) returns (MsgRevealBidResponse) { option (google.api.http).post = "/cerc/auction/v1/reveal_bid"; }; + + // UpdateParams defines an operation for updating the x/staking module + // parameters. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); + + // ReleaseFunds is the command for paying the winners of provider auctions + rpc ReleaseFunds(MsgReleaseFunds) returns (MsgReleaseFundsResponse) { + option (google.api.http).post = "/cerc/auction/v1/release_funds"; + }; } // MsgCreateAuction defines a create auction message @@ -36,41 +46,56 @@ message MsgCreateAuction { option (gogoproto.goproto_getters) = false; option (cosmos.msg.v1.signer) = "signer"; + // Address of the signer + string signer = 1 + [ (gogoproto.moretags) = "json:\"signer\" yaml:\"signer\"" ]; + + // Auction kind (vickrey | provider) + string kind = 2 [ (gogoproto.moretags) = "json:\"kind\" yaml:\"kind\"" ]; + // Duration of the commits phase in seconds - google.protobuf.Duration commits_duration = 1 [ + google.protobuf.Duration commits_duration = 3 [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "json:\"commits_duration\" yaml:\"commits_duration\"" ]; // Duration of the reveals phase in seconds - google.protobuf.Duration reveals_duration = 2 [ + google.protobuf.Duration reveals_duration = 4 [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "json:\"reveals_duration\" yaml:\"reveals_duration\"" ]; // Commit fees - cosmos.base.v1beta1.Coin commit_fee = 3 [ + cosmos.base.v1beta1.Coin commit_fee = 5 [ (gogoproto.nullable) = false, (gogoproto.moretags) = "json:\"commit_fee\" yaml:\"commit_fee\"" ]; // Reveal fees - cosmos.base.v1beta1.Coin reveal_fee = 4 [ + cosmos.base.v1beta1.Coin reveal_fee = 6 [ (gogoproto.nullable) = false, (gogoproto.moretags) = "json:\"reveal_fee\" yaml:\"reveal_fee\"" ]; // Minimum acceptable bid amount - cosmos.base.v1beta1.Coin minimum_bid = 5 [ + // Only applicable in vickrey auctions + cosmos.base.v1beta1.Coin minimum_bid = 7 [ (gogoproto.nullable) = false, (gogoproto.moretags) = "json:\"minimum_bid\" yaml:\"minimum_bid\"" ]; - // Address of the signer - string signer = 6 - [ (gogoproto.moretags) = "json:\"signer\" yaml:\"signer\"" ]; + // Maximum acceptable bid amount + // Only applicable in provider auctions + cosmos.base.v1beta1.Coin max_price = 8 [ + (gogoproto.nullable) = false, + (gogoproto.moretags) = "json:\"max_price\" yaml:\"max_price\"" + ]; + + // Number of desired providers (num of auction winners) + // Only applicable in provider auctions + int32 num_providers = 9; } // MsgCreateAuctionResponse returns the details of the created auction @@ -134,3 +159,44 @@ message MsgRevealBidResponse { Auction auction = 1 [ (gogoproto.moretags) = "json:\"auction\" yaml:\"auction\"" ]; } + +// MsgUpdateParams is the Msg/UpdateParams request type. +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address that controls the module (defaults to x/gov unless + // overwritten). + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // params defines the x/auction parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [ (gogoproto.nullable) = false ]; +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +message MsgUpdateParamsResponse {}; + +// ReleaseFunds defines the message to pay the winners of provider auctions +message MsgReleaseFunds { + option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "signer"; + + // Auction id + string auction_id = 1 + [ (gogoproto.moretags) = "json:\"auction_id\" yaml:\"auction_id\"" ]; + + // Address of the signer + string signer = 2 + [ (gogoproto.moretags) = "json:\"signer\" yaml:\"signer\"" ]; +} + +// MsgReleaseFundsResponse returns the state of the auction after releasing the funds +message MsgReleaseFundsResponse { + option (gogoproto.goproto_getters) = false; + + // Auction details + Auction auction = 1 + [ (gogoproto.moretags) = "json:\"auction\" yaml:\"auction\"" ]; +} diff --git a/proto/cerc/bond/module/v1/module.proto b/proto/cerc/bond/module/v1/module.proto index ea52203..6595074 100644 --- a/proto/cerc/bond/module/v1/module.proto +++ b/proto/cerc/bond/module/v1/module.proto @@ -10,4 +10,8 @@ message Module { option (cosmos.app.v1alpha1.module) = { go_import : "git.vdb.to/cerc-io/laconicd/x/bond" }; + + // authority defines the custom module authority. If not set, defaults to the + // governance module. + string authority = 2; } diff --git a/proto/cerc/bond/v1/tx.proto b/proto/cerc/bond/v1/tx.proto index 1812b10..83d8cde 100644 --- a/proto/cerc/bond/v1/tx.proto +++ b/proto/cerc/bond/v1/tx.proto @@ -6,6 +6,8 @@ import "cosmos/msg/v1/msg.proto"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; +import "cerc/bond/v1/bond.proto"; option go_package = "git.vdb.to/cerc-io/laconicd/x/bond"; @@ -32,6 +34,10 @@ service Msg { rpc CancelBond(MsgCancelBond) returns (MsgCancelBondResponse) { option (google.api.http).post = "/cerc/bond/v1/cancel_bond"; }; + + // UpdateParams defines an operation for updating the x/staking module + // parameters. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); } // MsgCreateBond defines a SDK message for creating a new bond. @@ -91,3 +97,21 @@ message MsgCancelBond { // MsgCancelBondResponse defines the Msg/CancelBond response type. message MsgCancelBondResponse {} + +// MsgUpdateParams is the Msg/UpdateParams request type. +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address that controls the module (defaults to x/gov unless + // overwritten). + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // params defines the x/bond parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [ (gogoproto.nullable) = false ]; +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +message MsgUpdateParamsResponse {}; diff --git a/proto/cerc/registry/module/v1/module.proto b/proto/cerc/registry/module/v1/module.proto index 2140d6a..fd82abe 100644 --- a/proto/cerc/registry/module/v1/module.proto +++ b/proto/cerc/registry/module/v1/module.proto @@ -10,4 +10,8 @@ message Module { option (cosmos.app.v1alpha1.module) = { go_import : "git.vdb.to/cerc-io/laconicd/x/registry" }; + + // authority defines the custom module authority. If not set, defaults to the + // governance module. + string authority = 2; } diff --git a/proto/cerc/registry/v1/tx.proto b/proto/cerc/registry/v1/tx.proto index b38c50f..184af8d 100644 --- a/proto/cerc/registry/v1/tx.proto +++ b/proto/cerc/registry/v1/tx.proto @@ -6,6 +6,7 @@ import "google/api/annotations.proto"; import "gogoproto/gogo.proto"; import "cosmos/msg/v1/msg.proto"; import "cerc/registry/v1/registry.proto"; +import "cosmos_proto/cosmos.proto"; option go_package = "git.vdb.to/cerc-io/laconicd/x/registry"; @@ -66,6 +67,10 @@ service Msg { returns (MsgSetAuthorityBondResponse) { option (google.api.http).post = "/cerc/registry/v1/set_authority_bond"; } + + // UpdateParams defines an operation for updating the x/staking module + // parameters. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); } // MsgSetRecord @@ -203,3 +208,21 @@ message MsgReassociateRecords { // MsgReassociateRecordsResponse message MsgReassociateRecordsResponse {} + +// MsgUpdateParams is the Msg/UpdateParams request type. +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address that controls the module (defaults to x/gov unless + // overwritten). + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // params defines the x/registry parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [ (gogoproto.nullable) = false ]; +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +message MsgUpdateParamsResponse {} diff --git a/src/auction.test.ts b/src/auction.test.ts index 4bec436..2f31b1b 100644 --- a/src/auction.test.ts +++ b/src/auction.test.ts @@ -1,71 +1,89 @@ import { Registry, Account, createBid } from './index'; -import { getConfig } from './testing/helper'; +import { createTestAccounts, getConfig } from './testing/helper'; import { DENOM } from './constants'; +import { INVALID_BID_ERROR, OWNER_MISMATCH_ERROR, RELEASE_FUNDS_ERROR } from './types/cerc/auction/message'; jest.setTimeout(30 * 60 * 1000); const { chainId, rpcEndpoint, gqlEndpoint, privateKey, fee } = getConfig(); -const auctionTests = (numBidders = 3) => { +const DURATION = 60; +const TX_FEES = Number(fee.amount[0].amount); +const COMMIT_FEE = '1000'; +const REVEAL_FEE = '1000'; + +const CREATOR_INITIAL_BALANCE = 1000000000000; +const BIDDER_INITIAL_BALANCE = 20000000; +const LOWEST_BID_AMOUNT = 10000000; + +const auctionTests = () => { let registry: Registry; - - const accounts: { address: string, privateKey: string, bid?: any }[] = []; - let auctionId: string; - let authorityName: string; + + let auctionCreatorAccount: { address: string, privateKey: string }; + let bidderAccounts: { address: string, privateKey: string, bid?: any }[] = []; + + const numBidders = 3; + let bidAmounts: string[] = []; beforeAll(async () => { - console.log('Running auction tests with num bidders', numBidders); - registry = new Registry(gqlEndpoint, rpcEndpoint, { chainId }); - }); - test('Setup bidder accounts', async () => { + // Create auction creator account + const [auctionCreator] = await createTestAccounts(1); + await registry.sendCoins({ denom: DENOM, amount: CREATOR_INITIAL_BALANCE.toString(), destinationAddress: auctionCreator.address }, privateKey, fee); + auctionCreatorAccount = { address: auctionCreator.address, privateKey: auctionCreator.privateKey.toString('hex') }; + + // Create bidder accounts + const accounts = await createTestAccounts(numBidders); for (let i = 0; i < numBidders; i++) { - const mnenonic = Account.generateMnemonic(); - const account = await Account.generateFromMnemonic(mnenonic); - await account.init(); - await registry.sendCoins({ denom: DENOM, amount: '20000000', destinationAddress: account.address }, privateKey, fee); - accounts.push({ address: account.address, privateKey: account.privateKey.toString('hex') }); + await registry.sendCoins({ denom: DENOM, amount: BIDDER_INITIAL_BALANCE.toString(), destinationAddress: accounts[i].address }, privateKey, fee); + bidderAccounts.push({ address: accounts[i].address, privateKey: accounts[i].privateKey.toString('hex') }); } }); - test('Reserve authority.', async () => { - authorityName = `laconic-${Date.now()}`; - await registry.reserveAuthority({ name: authorityName }, accounts[0].privateKey, fee); - }); + test('Create a vickrey auction', async () => { + const minimumBid = '1000000'; - test('Authority should be under auction.', async () => { - const [record] = await registry.lookupAuthorities([authorityName], true); - expect(record.ownerAddress).toEqual(''); - expect(record.height).toBeDefined(); - expect(record.status).toEqual('auction'); + const auction = await registry.createAuction( + { + commitsDuration: DURATION.toString(), + revealsDuration: DURATION.toString(), + denom: DENOM, + commitFee: COMMIT_FEE, + revealFee: REVEAL_FEE, + minimumBid + }, + auctionCreatorAccount.privateKey, + fee + ); - expect(record.auction.id).toBeDefined(); - expect(record.auction.status).toEqual('commit'); + expect(auction.auction?.id).toBeDefined(); - auctionId = record.auction.id; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + auctionId = auction.auction!.id; + expect(auction.auction?.status).toEqual('commit'); }); test('Commit bids.', async () => { for (let i = 0; i < numBidders; i++) { - accounts[i].bid = await createBid(chainId, auctionId, accounts[i].address, `${10000000 + (i * 500)}${DENOM}`); - await registry.commitBid({ auctionId, commitHash: accounts[i].bid.commitHash }, accounts[i].privateKey, fee); + bidAmounts.push((LOWEST_BID_AMOUNT + (i * 500)).toString()); + bidderAccounts[i].bid = await createBid(chainId, auctionId, bidderAccounts[i].address, `${bidAmounts[i]}${DENOM}`); + await registry.commitBid({ auctionId, commitHash: bidderAccounts[i].bid.commitHash }, bidderAccounts[i].privateKey, fee); } - }); - test('Check bids are committed', async () => { - const [record] = await registry.lookupAuthorities([authorityName], true); - expect(record.auction.id).toBeDefined(); - expect(record.auction.status).toEqual('commit'); - expect(record.auction.bids).toHaveLength(accounts.length); - - record.auction.bids.forEach((bid: any) => { + const [auction] = await registry.getAuctionsByIds([auctionId]); + expect(auction.status).toEqual('commit'); + expect(auction.bids.length).toEqual(3); + auction.bids.forEach((bid: any) => { expect(bid.status).toEqual('commit'); }); }); test('Wait for reveal phase.', (done) => { - setTimeout(done, 60 * 1000); + const commitTime = DURATION * 1000; + const waitTime = commitTime + (6 * 1000); + + setTimeout(done, waitTime); }); test('Reveal bids.', async () => { @@ -73,7 +91,168 @@ const auctionTests = (numBidders = 3) => { expect(auction.status).toEqual('reveal'); for (let i = 0; i < numBidders; i++) { - await registry.revealBid({ auctionId, reveal: accounts[i].bid.revealString }, accounts[i].privateKey, fee); + await registry.revealBid({ auctionId, reveal: bidderAccounts[i].bid.revealString }, bidderAccounts[i].privateKey, fee); + } + }); + + test('Check bids are revealed', async () => { + const [auction] = await registry.getAuctionsByIds([auctionId]); + expect(auction.status).toEqual('reveal'); + auction.bids.forEach((bid: any) => { + expect(bid.status).toEqual('reveal'); + }); + + const expectedBidAmounts = bidAmounts.map(bidAmount => { return { quantity: bidAmount.toString(), type: DENOM }; }); + const actualBidAmounts = auction.bids.map((bid: any) => bid.bidAmount); + expect(actualBidAmounts).toEqual(expect.arrayContaining(expectedBidAmounts)); + + // Check that the bid amounts are locked after reveal phase + for (let i = 0; i < numBidders; i++) { + // The bid amount, commit fee, reveal fee and tx fees (for commit and reveal txs) will be deducted from the bidder's acoount + const expectedBalance = BIDDER_INITIAL_BALANCE - parseInt(bidAmounts[i]) - (2 * TX_FEES) - parseInt(COMMIT_FEE) - parseInt(REVEAL_FEE); + await checkAccountBalance(registry, bidderAccounts[i].address, expectedBalance); + } + }); + + test('Wait for auction completion.', (done) => { + const revealTime = DURATION * 1000; + const waitTime = revealTime + (6 * 1000); + + setTimeout(done, waitTime); + }); + + test('Check auction status, winner address and balance.', async () => { + const [auction] = await registry.getAuctionsByIds([auctionId]); + expect(auction.status).toEqual('completed'); + + const highestBidder = bidderAccounts[bidderAccounts.length - 1]; + const secondHighestBidder = bidderAccounts[bidderAccounts.length - 2]; + + expect(auction.winnerAddresses).toHaveLength(1); + + expect(auction.winnerAddresses[0]).toEqual(highestBidder.address); + expect(highestBidder.bid.reveal.bidAmount).toEqual(`${auction.winnerBids[0].quantity}${auction.winnerBids[0].type}`); + expect(secondHighestBidder.bid.reveal.bidAmount).toEqual(`${auction.winnerPrice.quantity}${auction.winnerPrice.type}`); + + const winningPriceAmount = parseInt(auction.winnerPrice.quantity); + + // The winning price will get deducted after auction completion + const expectedBalance = BIDDER_INITIAL_BALANCE - winningPriceAmount - (2 * TX_FEES) - parseInt(COMMIT_FEE); + await checkAccountBalance(registry, highestBidder.address, expectedBalance); + }); +}; + +const providerAuctionTestsWithBids = (bidAmounts: number[], numProviders: number) => { + let registry: Registry; + let auctionId: string; + + let auctionCreatorAccount: { address: string, privateKey: string }; + let bidderAccounts: { address: string, privateKey: string, bid?: any }[] = []; + let sortedBidders: { address: string, privateKey: string, bid?: any }[] = []; + let filteredBidders: { address: string, privateKey: string, bid?: any }[] = []; + let invalidBidderAddress: string; + let otherAccount: { address: string, privateKey: string }; + + const numBidders = bidAmounts.length; + const maxPrice = 10 * LOWEST_BID_AMOUNT; + + beforeAll(async () => { + registry = new Registry(gqlEndpoint, rpcEndpoint, { chainId }); + + // Create auction creator account + const [auctionCreator] = await createTestAccounts(1); + await registry.sendCoins({ denom: DENOM, amount: CREATOR_INITIAL_BALANCE.toString(), destinationAddress: auctionCreator.address }, privateKey, fee); + auctionCreatorAccount = { address: auctionCreator.address, privateKey: auctionCreator.privateKey.toString('hex') }; + + // Create bidder accounts + const accounts = await createTestAccounts(numBidders); + for (let i = 0; i < numBidders; i++) { + await registry.sendCoins({ denom: DENOM, amount: BIDDER_INITIAL_BALANCE.toString(), destinationAddress: accounts[i].address }, privateKey, fee); + bidderAccounts.push({ address: accounts[i].address, privateKey: accounts[i].privateKey.toString('hex') }); + } + + const mnenonic3 = Account.generateMnemonic(); + const otherAcc = await Account.generateFromMnemonic(mnenonic3); + await otherAcc.init(); + + await registry.sendCoins({ denom: DENOM, amount: CREATOR_INITIAL_BALANCE.toString(), destinationAddress: otherAcc.address }, privateKey, fee); + otherAccount = { address: otherAcc.address, privateKey: otherAcc.privateKey.toString('hex') }; + }); + + test('Create a provider auction', async () => { + const auction = await registry.createProviderAuction( + { + commitsDuration: DURATION.toString(), + revealsDuration: DURATION.toString(), + denom: DENOM, + commitFee: COMMIT_FEE, + revealFee: REVEAL_FEE, + maxPrice: maxPrice.toString(), + numProviders + }, + auctionCreatorAccount.privateKey, fee + ); + + expect(auction.auction?.id).toBeDefined(); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + auctionId = auction.auction!.id; + expect(auction.auction?.status).toEqual('commit'); + + // The locked amount and tx fees are deducted from the auction creator's account + const expectedBalance = CREATOR_INITIAL_BALANCE - (maxPrice * numProviders) - TX_FEES; + await checkAccountBalance(registry, auctionCreatorAccount.address, expectedBalance); + }); + + test('Commit bids.', async () => { + for (let i = 0; i < numBidders; i++) { + bidderAccounts[i].bid = await createBid(chainId, auctionId, bidderAccounts[i].address, `${bidAmounts[i]}${DENOM}`); + await registry.commitBid({ auctionId, commitHash: bidderAccounts[i].bid.commitHash }, bidderAccounts[i].privateKey, fee); + } + + sortedBidders = bidderAccounts.slice().sort((a, b) => { + return parseInt(a.bid.reveal.bidAmount) - parseInt(b.bid.reveal.bidAmount); + }); + + for (const bidder of sortedBidders) { + if (parseInt(bidder.bid.reveal.bidAmount) > maxPrice) { + invalidBidderAddress = bidder.address; + } + } + + const [auction] = await registry.getAuctionsByIds([auctionId]); + expect(auction.status).toEqual('commit'); + expect(auction.bids.length).toEqual(numBidders); + auction.bids.forEach((bid: any) => { + expect(bid.status).toEqual('commit'); + }); + }); + + test('Wait for reveal phase.', (done) => { + const commitTime = DURATION * 1000; + const waitTime = commitTime + (6 * 1000); + + setTimeout(done, waitTime); + }); + + test('Reveal bids.', async () => { + const [auction] = await registry.getAuctionsByIds([auctionId]); + expect(auction.status).toEqual('reveal'); + + for (let i = 0; i < numBidders; i++) { + try { + await registry.revealBid( + { auctionId, reveal: bidderAccounts[i].bid.revealString }, + bidderAccounts[i].privateKey, + fee + ); + } catch (error: any) { + if (invalidBidderAddress === bidderAccounts[i].address) { + expect(error.toString()).toContain(INVALID_BID_ERROR); + } else { + throw error; + } + } } }); @@ -81,49 +260,128 @@ const auctionTests = (numBidders = 3) => { const [auction] = await registry.getAuctionsByIds([auctionId]); expect(auction.status).toEqual('reveal'); - auction.bids.forEach((bid: any) => { - expect(bid.status).toEqual('reveal'); + const expectedBids = sortedBidders.map((bidder: any) => { + if (invalidBidderAddress === bidder.address) { + return '0'; + } + return bidder.bid.reveal.bidAmount; }); + + const actualBidAmounts = auction.bids.map((bid: any) => `${bid.bidAmount.quantity}${bid.bidAmount.type}`); + expect(actualBidAmounts).toEqual(expect.arrayContaining(expectedBids)); }); test('Wait for auction completion.', (done) => { - setTimeout(done, 60 * 1000); + const revealTime = DURATION * 1000; + const waitTime = revealTime + (6 * 1000); + + setTimeout(done, waitTime); }); - test('Check auction winner, authority owner and status.', async () => { + test('Check auction status, winners and creator balance.', async () => { const [auction] = await registry.getAuctionsByIds([auctionId]); expect(auction.status).toEqual('completed'); - const highestBidder = accounts[accounts.length - 1]; - const secondHighestBidder = (accounts.length > 1 ? accounts[accounts.length - 2] : highestBidder); + // Filter bidders to exclude bids exceeding maxPrice + filteredBidders = sortedBidders.filter(bidder => { + return parseInt(bidder.bid.reveal.bidAmount) <= parseInt(`${maxPrice}${DENOM}`); + }); - expect(auction.winnerAddress).toEqual(highestBidder.address); - expect(highestBidder.bid.reveal.bidAmount).toEqual(`${auction.winnerBid.quantity}${auction.winnerBid.type}`); - expect(secondHighestBidder.bid.reveal.bidAmount).toEqual(`${auction.winnerPrice.quantity}${auction.winnerPrice.type}`); + const numWinners = Math.min(filteredBidders.length, numProviders); + const winningBidders = filteredBidders.slice(0, numWinners); - const [record] = await registry.lookupAuthorities([authorityName], true); - expect(record.ownerAddress).toEqual(highestBidder.address); - expect(record.height).toBeDefined(); - expect(record.status).toEqual('active'); + // Check winner price is equal to highest winning bid + const expectedWinningPrice = winningBidders[numWinners - 1].bid.reveal.bidAmount; + expect(`${auction.winnerPrice.quantity}${auction.winnerPrice.type}`).toEqual(expectedWinningPrice); + + // The total winning amount will get deducted and the leftover locked amount is returned + const totalWinningAmount = parseInt(auction.winnerPrice.quantity) * winningBidders.length; + const expectedCreatorBalance = CREATOR_INITIAL_BALANCE - totalWinningAmount - TX_FEES; + await checkAccountBalance(registry, auctionCreatorAccount.address, expectedCreatorBalance); + + // Funds should not be given to winners on auction completion + for (let i = 0; i < winningBidders.length; i++) { + const bidWinner = winningBidders[i]; + + expect(auction.winnerAddresses[i]).toEqual(bidWinner.address); + expect(`${auction.winnerBids[i].quantity}${auction.winnerBids[i].type}`).toEqual(bidWinner.bid.reveal.bidAmount); + + const expectedBidderBalance = BIDDER_INITIAL_BALANCE - (2 * TX_FEES) - parseInt(COMMIT_FEE); + await checkAccountBalance(registry, bidWinner.address, expectedBidderBalance); + } + }); + + test('Release funds and check provider balances.', async () => { + const [auction] = await registry.getAuctionsByIds([auctionId]); + + const numWinners = Math.min(filteredBidders.length, numProviders); + const winningBidders = filteredBidders.slice(0, numWinners); + const losingBidders = bidderAccounts.filter(bidder => !winningBidders.includes(bidder)); + + const winningBidAmount = parseInt(auction.winnerPrice.quantity); + + // Only owner can release funds + try { + await registry.releaseFunds({ auctionId }, otherAccount.privateKey, fee); + } catch (error: any) { + expect(error.toString()).toContain(OWNER_MISMATCH_ERROR); + } + + // Release funds + const auctionObj = await registry.releaseFunds({ auctionId }, auctionCreatorAccount.privateKey, fee); + expect(auctionObj.auction?.fundsReleased).toBe(true); + + // Auction winners get the winning amount after funds are released + const expectedBidderBalance = BIDDER_INITIAL_BALANCE + winningBidAmount - (2 * TX_FEES) - parseInt(COMMIT_FEE); + for (let i = 0; i < winningBidders.length; i++) { + const bidWinner = winningBidders[i]; + + expect(auction.winnerAddresses[i]).toEqual(bidWinner.address); + expect(`${auction.winnerBids[i].quantity}${auction.winnerBids[i].type}`).toEqual(bidWinner.bid.reveal.bidAmount); + + await checkAccountBalance(registry, bidWinner.address, expectedBidderBalance); + } + + // Check balances of non-winners + for (const bidder of losingBidders) { + let expectedBidderBalance: number; + if (invalidBidderAddress !== bidder.address) { + expectedBidderBalance = BIDDER_INITIAL_BALANCE - (2 * TX_FEES) - parseInt(COMMIT_FEE); + } else { + // Bid is invalid, reveal fees are not returned + expectedBidderBalance = BIDDER_INITIAL_BALANCE - (2 * TX_FEES) - parseInt(COMMIT_FEE) - parseInt(REVEAL_FEE); + } + + await checkAccountBalance(registry, bidder.address, expectedBidderBalance); + } + + // Funds cannot be released twice + try { + await registry.releaseFunds({ auctionId }, auctionCreatorAccount.privateKey, fee); + } catch (error: any) { + expect(error.toString()).toContain(RELEASE_FUNDS_ERROR); + } }); }; -const withNumBidders = (numBidders: number) => () => auctionTests(numBidders); +const checkAccountBalance = async (registry: Registry, address: string, expectedBalance: number): Promise => { + const [winnerAccountObj] = await registry.getAccounts([address]); + const [{ type, quantity }] = winnerAccountObj.balance; + const actualBalance = parseInt(quantity); -if (!process.env.TEST_AUCTIONS_ENABLED) { - // Required as jest complains if file has no tests. - test('skipping auction tests', () => {}); -} else { - /** - Running these tests requires name auctions enabled. In laconicd repo run: + expect(actualBalance).toBe(expectedBalance); + expect(type).toBe(DENOM); +}; - TEST_AUCTION_ENABLED=true ./init.sh +const providerAuctionTests = () => { + // With a bid amount greater than the max price + const bids = [10002000, 10009000, 10006000 * 10, 10004000]; - Run tests: + // Number of providers is less than number of bidders + describe('Auction with numProviders < numBidders', () => providerAuctionTestsWithBids(bids, 3)); + // Number of providers is greater than number of bidders + describe('Auction numProviders > numBidders', () => providerAuctionTestsWithBids(bids, 5)); +}; - yarn test:auctions - */ - describe('Auction (1 bidder)', withNumBidders(1)); - describe('Auction (2 bidders)', withNumBidders(2)); - describe('Auction (4 bidders)', withNumBidders(4)); -} +describe('Vickrey Auction', () => auctionTests()); +describe('Provider Auction', () => providerAuctionTests()); diff --git a/src/authority-auction.test.ts b/src/authority-auction.test.ts new file mode 100644 index 0000000..6fcfffb --- /dev/null +++ b/src/authority-auction.test.ts @@ -0,0 +1,139 @@ +import { Registry, Account, createBid } from './index'; +import { getConfig } from './testing/helper'; +import { DENOM } from './constants'; + +jest.setTimeout(30 * 60 * 1000); +const { chainId, rpcEndpoint, gqlEndpoint, privateKey, fee } = getConfig(); + +const DURATION = 60; + +const auctionTests = (numBidders = 3) => { + let registry: Registry; + + const accounts: { address: string, privateKey: string, bid?: any }[] = []; + + let auctionId: string; + let authorityName: string; + + beforeAll(async () => { + console.log('Running auction tests with num bidders', numBidders); + + registry = new Registry(gqlEndpoint, rpcEndpoint, { chainId }); + }); + + test('Setup bidder accounts', async () => { + for (let i = 0; i < numBidders; i++) { + const mnenonic = Account.generateMnemonic(); + const account = await Account.generateFromMnemonic(mnenonic); + await account.init(); + await registry.sendCoins({ denom: DENOM, amount: '20000000', destinationAddress: account.address }, privateKey, fee); + accounts.push({ address: account.address, privateKey: account.privateKey.toString('hex') }); + } + }); + + test('Reserve authority.', async () => { + authorityName = `laconic-${Date.now()}`; + await registry.reserveAuthority({ name: authorityName }, accounts[0].privateKey, fee); + }); + + test('Authority should be under auction.', async () => { + const [record] = await registry.lookupAuthorities([authorityName], true); + expect(record.ownerAddress).toEqual(''); + expect(record.height).toBeDefined(); + expect(record.status).toEqual('auction'); + + expect(record.auction.id).toBeDefined(); + expect(record.auction.status).toEqual('commit'); + + auctionId = record.auction.id; + }); + + test('Commit bids.', async () => { + for (let i = 0; i < numBidders; i++) { + accounts[i].bid = await createBid(chainId, auctionId, accounts[i].address, `${10000000 + (i * 500)}${DENOM}`); + await registry.commitBid({ auctionId, commitHash: accounts[i].bid.commitHash }, accounts[i].privateKey, fee); + } + }); + + test('Check bids are committed', async () => { + const [record] = await registry.lookupAuthorities([authorityName], true); + expect(record.auction.id).toBeDefined(); + expect(record.auction.status).toEqual('commit'); + expect(record.auction.bids).toHaveLength(accounts.length); + + record.auction.bids.forEach((bid: any) => { + expect(bid.status).toEqual('commit'); + }); + }); + + test('Wait for reveal phase.', (done) => { + const commitTime = DURATION * 1000; + const waitTime = commitTime + (6 * 1000); + + setTimeout(done, waitTime); + }); + + test('Reveal bids.', async () => { + const [auction] = await registry.getAuctionsByIds([auctionId]); + expect(auction.status).toEqual('reveal'); + + for (let i = 0; i < numBidders; i++) { + await registry.revealBid({ auctionId, reveal: accounts[i].bid.revealString }, accounts[i].privateKey, fee); + } + }); + + test('Check bids are revealed', async () => { + const [auction] = await registry.getAuctionsByIds([auctionId]); + expect(auction.status).toEqual('reveal'); + + auction.bids.forEach((bid: any) => { + expect(bid.status).toEqual('reveal'); + }); + }); + + test('Wait for auction completion.', (done) => { + const revealTime = DURATION * 1000; + const waitTime = revealTime + (6 * 1000); + + setTimeout(done, waitTime); + }); + + test('Check auction winner, authority owner and status.', async () => { + const [auction] = await registry.getAuctionsByIds([auctionId]); + expect(auction.status).toEqual('completed'); + + const highestBidder = accounts[accounts.length - 1]; + const secondHighestBidder = (accounts.length > 1 ? accounts[accounts.length - 2] : highestBidder); + + expect(auction.winnerAddresses).toHaveLength(1); + + expect(auction.winnerAddresses[0]).toEqual(highestBidder.address); + expect(`${auction.winnerBids[0].quantity}${auction.winnerBids[0].type}`).toEqual(highestBidder.bid.reveal.bidAmount); + expect(`${auction.winnerPrice.quantity}${auction.winnerPrice.type}`).toEqual(secondHighestBidder.bid.reveal.bidAmount); + + const [record] = await registry.lookupAuthorities([authorityName], true); + expect(record.ownerAddress).toEqual(highestBidder.address); + expect(record.height).toBeDefined(); + expect(record.status).toEqual('active'); + }); +}; + +const withNumBidders = (numBidders: number) => () => auctionTests(numBidders); + +if (!process.env.TEST_AUCTIONS_ENABLED) { + // Required as jest complains if file has no tests. + test('skipping auction tests', () => {}); +} else { + /** + Running these tests requires name auctions enabled. In laconicd repo run: + + TEST_AUCTION_ENABLED=true ./init.sh + + Run tests: + + yarn test:auctions + */ + describe('Auction (1 bidder)', withNumBidders(1)); + describe('Auction (2 bidders)', withNumBidders(2)); + describe('Auction (4 bidders)', withNumBidders(4)); +} diff --git a/src/constants.ts b/src/constants.ts index a54157d..1fa6289 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,2 +1,5 @@ export const DENOM = 'alnt'; export const DEFAULT_GAS_ESTIMATION_MULTIPLIER = 2; + +export const AUCTION_KIND_VICKREY = 'vickrey'; +export const AUCTION_KIND_PROVIDER = 'provider'; diff --git a/src/index.ts b/src/index.ts index 4ea8b7d..29c9721 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,7 +22,10 @@ import { } from './types/cerc/registry/message'; import { MessageMsgCommitBid, - MessageMsgRevealBid + MessageMsgRevealBid, + MessageCreateVickreyAuction, + MessageCreateProviderAuction, + MessageMsgReleaseFunds } from './types/cerc/auction/message'; import { MessageMsgSendCoins } from './types/cosmos/bank/message'; import { MessageMsgOnboardParticipant } from './types/cerc/onboarding/message'; @@ -31,7 +34,8 @@ import { Coin } from './proto/cosmos/base/v1beta1/coin'; import { MsgCancelBondResponse, MsgCreateBondResponse, MsgRefillBondResponse, MsgWithdrawBondResponse } from './proto/cerc/bond/v1/tx'; import { MsgOnboardParticipantResponse } from './proto/cerc/onboarding/v1/tx'; import { MsgSendResponse } from './proto/cosmos/bank/v1beta1/tx'; -import { DEFAULT_GAS_ESTIMATION_MULTIPLIER } from './constants'; +import { AUCTION_KIND_PROVIDER, AUCTION_KIND_VICKREY, DEFAULT_GAS_ESTIMATION_MULTIPLIER } from './constants'; +import { MsgCreateAuctionResponse } from './proto/cerc/auction/v1/tx'; /** * Create an auction bid. @@ -455,7 +459,7 @@ export class Registry { /** * Onboard participant. - */ + */ async onboardParticipant ({ ethPayload, ethSignature, role, kycId }: MessageMsgOnboardParticipant, privateKey: string, fee: StdFee | number = DEFAULT_GAS_ESTIMATION_MULTIPLIER): Promise { const account = new Account(Buffer.from(privateKey, 'hex')); await account.init(); @@ -472,22 +476,22 @@ export class Registry { } /** - * Query participants. - */ + * Query participants. + */ async getParticipants () { return this._client.getParticipants(); } /** * Get participant by cosmos (laconic) address. - */ + */ async getParticipantByAddress (address: string) { return this._client.getParticipantByAddress(address); } /** * Get participant by nitro address. - */ + */ async getParticipantByNitroAddress (nitroAddress: string) { return this._client.getParticipantByNitroAddress(nitroAddress); } @@ -499,6 +503,84 @@ export class Registry { { gasPrice: this._gasPrice } ); } + + async createAuction ( + { + commitsDuration, + revealsDuration, + denom, + commitFee, + revealFee, + minimumBid + }: MessageCreateVickreyAuction, + privateKey: string, + fee: StdFee | number = DEFAULT_GAS_ESTIMATION_MULTIPLIER + ): Promise { + const account = new Account(Buffer.from(privateKey, 'hex')); + await account.init(); + const laconicClient = await this.getLaconicClient(account); + + return laconicClient.createAuction( + account.address, + AUCTION_KIND_VICKREY, + commitsDuration, + revealsDuration, + denom, + commitFee, + revealFee, + minimumBid, + '', + 0, + fee + ); + } + + async createProviderAuction ( + { + commitsDuration, + revealsDuration, + denom, + commitFee, + revealFee, + maxPrice, + numProviders + }: MessageCreateProviderAuction, + privateKey: string, + fee: StdFee | number = DEFAULT_GAS_ESTIMATION_MULTIPLIER + ): Promise { + const account = new Account(Buffer.from(privateKey, 'hex')); + await account.init(); + const laconicClient = await this.getLaconicClient(account); + + return laconicClient.createAuction( + account.address, + AUCTION_KIND_PROVIDER, + commitsDuration, + revealsDuration, + denom, + commitFee, + revealFee, + '', + maxPrice, + numProviders, + fee + ); + } + + /** + * Release provider auction winner funds. + */ + async releaseFunds ({ auctionId }: MessageMsgReleaseFunds, privateKey: string, fee: StdFee | number = DEFAULT_GAS_ESTIMATION_MULTIPLIER) { + const account = new Account(Buffer.from(privateKey, 'hex')); + await account.init(); + const laconicClient = await this.getLaconicClient(account); + + return laconicClient.releaseFunds( + account.address, + auctionId, + fee + ); + } } export { Account }; diff --git a/src/laconic-client.ts b/src/laconic-client.ts index ae8f90c..2aaaf31 100644 --- a/src/laconic-client.ts +++ b/src/laconic-client.ts @@ -1,3 +1,4 @@ +import Long from 'long'; import { GeneratedType, OfflineSigner, Registry } from '@cosmjs/proto-signing'; import { @@ -12,17 +13,18 @@ import { Comet38Client } from '@cosmjs/tendermint-rpc'; import { MsgCancelBondEncodeObject, MsgCreateBondEncodeObject, MsgRefillBondEncodeObject, MsgWithdrawBondEncodeObject, bondTypes, typeUrlMsgCancelBond, typeUrlMsgCreateBond, typeUrlMsgRefillBond, typeUrlMsgWithdrawBond } from './types/cerc/bond/message'; import { Coin } from './proto/cosmos/base/v1beta1/coin'; import { MsgAssociateBondEncodeObject, MsgDeleteNameEncodeObject, MsgDissociateBondEncodeObject, MsgDissociateRecordsEncodeObject, MsgReassociateRecordsEncodeObject, MsgReserveAuthorityEncodeObject, MsgSetAuthorityBondEncodeObject, MsgSetNameEncodeObject, MsgSetRecordEncodeObject, registryTypes, typeUrlMsgAssociateBond, typeUrlMsgDeleteName, typeUrlMsgDissociateBond, typeUrlMsgDissociateRecords, typeUrlMsgReassociateRecords, typeUrlMsgReserveAuthority, typeUrlMsgSetAuthorityBond, typeUrlMsgSetName, typeUrlMsgSetRecord, NAMESERVICE_ERRORS } from './types/cerc/registry/message'; -import { MsgCommitBidEncodeObject, MsgRevealBidEncodeObject, auctionTypes, typeUrlMsgCommitBid, typeUrlMsgRevealBid } from './types/cerc/auction/message'; +import { AUCTION_ERRORS, MsgCommitBidEncodeObject, MsgCreateAuctionEncodeObject, MsgReleaseFundsEncodeObject, MsgRevealBidEncodeObject, auctionTypes, typeUrlMsgCommitBid, typeUrlMsgCreateAuction, typeUrlMsgReleaseFunds, typeUrlMsgRevealBid } from './types/cerc/auction/message'; import { MsgOnboardParticipantEncodeObject, ONBOARDING_DISABLED_ERROR, onboardingTypes, typeUrlMsgOnboardParticipant } from './types/cerc/onboarding/message'; import { MsgAssociateBondResponse, MsgDeleteNameResponse, MsgDissociateBondResponse, MsgDissociateRecordsResponse, MsgReassociateRecordsResponse, MsgReserveAuthorityResponse, MsgSetAuthorityBondResponse, MsgSetNameResponse, MsgSetRecordResponse, Payload } from './proto/cerc/registry/v1/tx'; import { Record, Signature } from './proto/cerc/registry/v1/registry'; import { Account } from './account'; import { Util } from './util'; -import { MsgCommitBidResponse, MsgRevealBidResponse } from './proto/cerc/auction/v1/tx'; +import { MsgCommitBidResponse, MsgCreateAuction, MsgCreateAuctionResponse, MsgReleaseFundsResponse, MsgRevealBidResponse } from './proto/cerc/auction/v1/tx'; import { MsgCancelBondResponse, MsgCreateBondResponse, MsgRefillBondResponse, MsgWithdrawBondResponse } from './proto/cerc/bond/v1/tx'; import { MsgOnboardParticipantResponse } from './proto/cerc/onboarding/v1/tx'; import { bankTypes } from './types/cosmos/bank/message'; import { EthPayload } from './proto/cerc/onboarding/v1/onboarding'; +import { Duration } from './proto/google/protobuf/duration'; const DEFAULT_WRITE_ERROR = 'Unable to write to laconicd.'; @@ -381,7 +383,7 @@ export class LaconicClient extends SigningStargateClient { } processWriteError (error: string) { - const errorMessage = [...NAMESERVICE_ERRORS, ONBOARDING_DISABLED_ERROR].find(message => error.includes(message)); + const errorMessage = [...NAMESERVICE_ERRORS, ...AUCTION_ERRORS, ONBOARDING_DISABLED_ERROR].find(message => error.includes(message)); if (!errorMessage) { console.error(error); @@ -413,4 +415,55 @@ export class LaconicClient extends SigningStargateClient { const response = await this.signAndBroadcast(signer, [onboardParticipantMsg], fee, memo); return this.parseResponse(response); } + + public async createAuction ( + signer: string, + kind: string, + commitsDuration: string, + revealsDuration: string, + denom: string, + commitFee: string, + revealFee: string, + minimumBid: string, + maxPrice: string, + numProviders: number, + fee: StdFee | 'auto' | number, + memo = '' + ) { + const createAuctionMsg: MsgCreateAuctionEncodeObject = { + typeUrl: typeUrlMsgCreateAuction, + value: { + signer, + kind, + commitsDuration: Duration.fromPartial({ seconds: Long.fromString(commitsDuration) }), + revealsDuration: Duration.fromPartial({ seconds: Long.fromString(revealsDuration) }), + commitFee: Coin.fromPartial({ amount: commitFee, denom }), + revealFee: Coin.fromPartial({ amount: revealFee, denom }), + minimumBid: Coin.fromPartial({ amount: minimumBid, denom }), + maxPrice: Coin.fromPartial({ amount: maxPrice, denom }), + numProviders + } + }; + + const response = await this.signAndBroadcast(signer, [createAuctionMsg], fee, memo); + return this.parseResponse(response); + } + + public async releaseFunds ( + signer: string, + auctionId: string, + fee: StdFee | 'auto' | number, + memo = '' + ) { + const createMsg: MsgReleaseFundsEncodeObject = { + typeUrl: typeUrlMsgReleaseFunds, + value: { + signer, + auctionId + } + }; + + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); + } } diff --git a/src/proto/cerc/auction/module/v1/module.ts b/src/proto/cerc/auction/module/v1/module.ts index 6573f98..267c220 100644 --- a/src/proto/cerc/auction/module/v1/module.ts +++ b/src/proto/cerc/auction/module/v1/module.ts @@ -8,14 +8,26 @@ export const protobufPackage = "cerc.auction.module.v1"; * Module is the app config object of the module. * Learn more: https://docs.cosmos.network/main/building-modules/depinject */ -export interface Module {} +export interface Module { + /** + * authority defines the custom module authority. If not set, defaults to the + * governance module. + */ + authority: string; +} function createBaseModule(): Module { - return {}; + return { authority: "" }; } export const Module = { - encode(_: Module, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + encode( + message: Module, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.authority !== "") { + writer.uint32(18).string(message.authority); + } return writer; }, @@ -26,6 +38,9 @@ export const Module = { while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { + case 2: + message.authority = reader.string(); + break; default: reader.skipType(tag & 7); break; @@ -34,17 +49,21 @@ export const Module = { return message; }, - fromJSON(_: any): Module { - return {}; + fromJSON(object: any): Module { + return { + authority: isSet(object.authority) ? String(object.authority) : "", + }; }, - toJSON(_: Module): unknown { + toJSON(message: Module): unknown { const obj: any = {}; + message.authority !== undefined && (obj.authority = message.authority); return obj; }, - fromPartial, I>>(_: I): Module { + fromPartial, I>>(object: I): Module { const message = createBaseModule(); + message.authority = object.authority ?? ""; return message; }, }; @@ -81,3 +100,7 @@ if (_m0.util.Long !== Long) { _m0.util.Long = Long as any; _m0.configure(); } + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/cerc/auction/v1/auction.ts b/src/proto/cerc/auction/v1/auction.ts index 350b2fc..b83241c 100644 --- a/src/proto/cerc/auction/v1/auction.ts +++ b/src/proto/cerc/auction/v1/auction.ts @@ -1,5 +1,4 @@ /* eslint-disable */ -import { Duration } from "../../../google/protobuf/duration"; import { Coin } from "../../../cosmos/base/v1beta1/coin"; import { Timestamp } from "../../../google/protobuf/timestamp"; import Long from "long"; @@ -8,22 +7,13 @@ import _m0 from "protobufjs/minimal"; export const protobufPackage = "cerc.auction.v1"; /** Params defines the auction module parameters */ -export interface Params { - /** Duration of the commits phase in seconds */ - commitsDuration?: Duration; - /** Duration of the reveals phase in seconds */ - revealsDuration?: Duration; - /** Commit fees */ - commitFee?: Coin; - /** Reveal fees */ - revealFee?: Coin; - /** Minimum acceptable bid amount */ - minimumBid?: Coin; -} +export interface Params {} /** Auction represents a sealed-bid on-chain auction */ export interface Auction { id: string; + /** Auction kind (vickrey | provider) */ + kind: string; status: string; /** Address of the creator of the auction */ ownerAddress: string; @@ -39,14 +29,37 @@ export interface Auction { */ commitFee?: Coin; revealFee?: Coin; - /** Minimum acceptable bid amount for a valid commit */ + /** + * Minimum acceptable bid amount for a valid commit + * Only applicable in vickrey auctions + */ minimumBid?: Coin; - /** Address of the winner */ - winnerAddress: string; - /** Winning bid, i.e., the highest bid */ - winningBid?: Coin; - /** Amount the winner pays, i.e. the second highest auction */ + /** + * Addresses of the winners + * (single winner for vickrey auction) + * (multiple winners for provider auctions) + */ + winnerAddresses: string[]; + /** Winning bids, i.e. the best bids */ + winningBids: Coin[]; + /** + * Auction winning price + * vickrey auction: second highest bid, paid by the winner + * provider auction: higest bid amongst winning_bids, paid by auction creator + * to each winner + */ winningPrice?: Coin; + /** + * Maximum acceptable bid amount for a valid commit + * Only applicable in provider auctions + */ + maxPrice?: Coin; + /** + * Number of desired providers (num of auction winners) + * Only applicable in provider auctions + */ + numProviders: number; + fundsReleased: boolean; } /** Auctions represent all the auctions in the module */ @@ -68,41 +81,11 @@ export interface Bid { } function createBaseParams(): Params { - return { - commitsDuration: undefined, - revealsDuration: undefined, - commitFee: undefined, - revealFee: undefined, - minimumBid: undefined, - }; + return {}; } export const Params = { - encode( - message: Params, - writer: _m0.Writer = _m0.Writer.create() - ): _m0.Writer { - if (message.commitsDuration !== undefined) { - Duration.encode( - message.commitsDuration, - writer.uint32(10).fork() - ).ldelim(); - } - if (message.revealsDuration !== undefined) { - Duration.encode( - message.revealsDuration, - writer.uint32(18).fork() - ).ldelim(); - } - if (message.commitFee !== undefined) { - Coin.encode(message.commitFee, writer.uint32(26).fork()).ldelim(); - } - if (message.revealFee !== undefined) { - Coin.encode(message.revealFee, writer.uint32(34).fork()).ldelim(); - } - if (message.minimumBid !== undefined) { - Coin.encode(message.minimumBid, writer.uint32(42).fork()).ldelim(); - } + encode(_: Params, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { return writer; }, @@ -113,21 +96,6 @@ export const Params = { while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { - case 1: - message.commitsDuration = Duration.decode(reader, reader.uint32()); - break; - case 2: - message.revealsDuration = Duration.decode(reader, reader.uint32()); - break; - case 3: - message.commitFee = Coin.decode(reader, reader.uint32()); - break; - case 4: - message.revealFee = Coin.decode(reader, reader.uint32()); - break; - case 5: - message.minimumBid = Coin.decode(reader, reader.uint32()); - break; default: reader.skipType(tag & 7); break; @@ -136,73 +104,17 @@ export const Params = { return message; }, - fromJSON(object: any): Params { - return { - commitsDuration: isSet(object.commitsDuration) - ? Duration.fromJSON(object.commitsDuration) - : undefined, - revealsDuration: isSet(object.revealsDuration) - ? Duration.fromJSON(object.revealsDuration) - : undefined, - commitFee: isSet(object.commitFee) - ? Coin.fromJSON(object.commitFee) - : undefined, - revealFee: isSet(object.revealFee) - ? Coin.fromJSON(object.revealFee) - : undefined, - minimumBid: isSet(object.minimumBid) - ? Coin.fromJSON(object.minimumBid) - : undefined, - }; + fromJSON(_: any): Params { + return {}; }, - toJSON(message: Params): unknown { + toJSON(_: Params): unknown { const obj: any = {}; - message.commitsDuration !== undefined && - (obj.commitsDuration = message.commitsDuration - ? Duration.toJSON(message.commitsDuration) - : undefined); - message.revealsDuration !== undefined && - (obj.revealsDuration = message.revealsDuration - ? Duration.toJSON(message.revealsDuration) - : undefined); - message.commitFee !== undefined && - (obj.commitFee = message.commitFee - ? Coin.toJSON(message.commitFee) - : undefined); - message.revealFee !== undefined && - (obj.revealFee = message.revealFee - ? Coin.toJSON(message.revealFee) - : undefined); - message.minimumBid !== undefined && - (obj.minimumBid = message.minimumBid - ? Coin.toJSON(message.minimumBid) - : undefined); return obj; }, - fromPartial, I>>(object: I): Params { + fromPartial, I>>(_: I): Params { const message = createBaseParams(); - message.commitsDuration = - object.commitsDuration !== undefined && object.commitsDuration !== null - ? Duration.fromPartial(object.commitsDuration) - : undefined; - message.revealsDuration = - object.revealsDuration !== undefined && object.revealsDuration !== null - ? Duration.fromPartial(object.revealsDuration) - : undefined; - message.commitFee = - object.commitFee !== undefined && object.commitFee !== null - ? Coin.fromPartial(object.commitFee) - : undefined; - message.revealFee = - object.revealFee !== undefined && object.revealFee !== null - ? Coin.fromPartial(object.revealFee) - : undefined; - message.minimumBid = - object.minimumBid !== undefined && object.minimumBid !== null - ? Coin.fromPartial(object.minimumBid) - : undefined; return message; }, }; @@ -210,6 +122,7 @@ export const Params = { function createBaseAuction(): Auction { return { id: "", + kind: "", status: "", ownerAddress: "", createTime: undefined, @@ -218,9 +131,12 @@ function createBaseAuction(): Auction { commitFee: undefined, revealFee: undefined, minimumBid: undefined, - winnerAddress: "", - winningBid: undefined, + winnerAddresses: [], + winningBids: [], winningPrice: undefined, + maxPrice: undefined, + numProviders: 0, + fundsReleased: false, }; } @@ -232,47 +148,59 @@ export const Auction = { if (message.id !== "") { writer.uint32(10).string(message.id); } + if (message.kind !== "") { + writer.uint32(18).string(message.kind); + } if (message.status !== "") { - writer.uint32(18).string(message.status); + writer.uint32(26).string(message.status); } if (message.ownerAddress !== "") { - writer.uint32(26).string(message.ownerAddress); + writer.uint32(34).string(message.ownerAddress); } if (message.createTime !== undefined) { Timestamp.encode( toTimestamp(message.createTime), - writer.uint32(34).fork() + writer.uint32(42).fork() ).ldelim(); } if (message.commitsEndTime !== undefined) { Timestamp.encode( toTimestamp(message.commitsEndTime), - writer.uint32(42).fork() + writer.uint32(50).fork() ).ldelim(); } if (message.revealsEndTime !== undefined) { Timestamp.encode( toTimestamp(message.revealsEndTime), - writer.uint32(50).fork() + writer.uint32(58).fork() ).ldelim(); } if (message.commitFee !== undefined) { - Coin.encode(message.commitFee, writer.uint32(58).fork()).ldelim(); + Coin.encode(message.commitFee, writer.uint32(66).fork()).ldelim(); } if (message.revealFee !== undefined) { - Coin.encode(message.revealFee, writer.uint32(66).fork()).ldelim(); + Coin.encode(message.revealFee, writer.uint32(74).fork()).ldelim(); } if (message.minimumBid !== undefined) { - Coin.encode(message.minimumBid, writer.uint32(74).fork()).ldelim(); + Coin.encode(message.minimumBid, writer.uint32(82).fork()).ldelim(); } - if (message.winnerAddress !== "") { - writer.uint32(82).string(message.winnerAddress); + for (const v of message.winnerAddresses) { + writer.uint32(90).string(v!); } - if (message.winningBid !== undefined) { - Coin.encode(message.winningBid, writer.uint32(90).fork()).ldelim(); + for (const v of message.winningBids) { + Coin.encode(v!, writer.uint32(98).fork()).ldelim(); } if (message.winningPrice !== undefined) { - Coin.encode(message.winningPrice, writer.uint32(98).fork()).ldelim(); + Coin.encode(message.winningPrice, writer.uint32(106).fork()).ldelim(); + } + if (message.maxPrice !== undefined) { + Coin.encode(message.maxPrice, writer.uint32(114).fork()).ldelim(); + } + if (message.numProviders !== 0) { + writer.uint32(120).int32(message.numProviders); + } + if (message.fundsReleased === true) { + writer.uint32(128).bool(message.fundsReleased); } return writer; }, @@ -288,44 +216,56 @@ export const Auction = { message.id = reader.string(); break; case 2: - message.status = reader.string(); + message.kind = reader.string(); break; case 3: - message.ownerAddress = reader.string(); + message.status = reader.string(); break; case 4: + message.ownerAddress = reader.string(); + break; + case 5: message.createTime = fromTimestamp( Timestamp.decode(reader, reader.uint32()) ); break; - case 5: + case 6: message.commitsEndTime = fromTimestamp( Timestamp.decode(reader, reader.uint32()) ); break; - case 6: + case 7: message.revealsEndTime = fromTimestamp( Timestamp.decode(reader, reader.uint32()) ); break; - case 7: + case 8: message.commitFee = Coin.decode(reader, reader.uint32()); break; - case 8: + case 9: message.revealFee = Coin.decode(reader, reader.uint32()); break; - case 9: + case 10: message.minimumBid = Coin.decode(reader, reader.uint32()); break; - case 10: - message.winnerAddress = reader.string(); - break; case 11: - message.winningBid = Coin.decode(reader, reader.uint32()); + message.winnerAddresses.push(reader.string()); break; case 12: + message.winningBids.push(Coin.decode(reader, reader.uint32())); + break; + case 13: message.winningPrice = Coin.decode(reader, reader.uint32()); break; + case 14: + message.maxPrice = Coin.decode(reader, reader.uint32()); + break; + case 15: + message.numProviders = reader.int32(); + break; + case 16: + message.fundsReleased = reader.bool(); + break; default: reader.skipType(tag & 7); break; @@ -337,6 +277,7 @@ export const Auction = { fromJSON(object: any): Auction { return { id: isSet(object.id) ? String(object.id) : "", + kind: isSet(object.kind) ? String(object.kind) : "", status: isSet(object.status) ? String(object.status) : "", ownerAddress: isSet(object.ownerAddress) ? String(object.ownerAddress) @@ -359,21 +300,31 @@ export const Auction = { minimumBid: isSet(object.minimumBid) ? Coin.fromJSON(object.minimumBid) : undefined, - winnerAddress: isSet(object.winnerAddress) - ? String(object.winnerAddress) - : "", - winningBid: isSet(object.winningBid) - ? Coin.fromJSON(object.winningBid) - : undefined, + winnerAddresses: Array.isArray(object?.winnerAddresses) + ? object.winnerAddresses.map((e: any) => String(e)) + : [], + winningBids: Array.isArray(object?.winningBids) + ? object.winningBids.map((e: any) => Coin.fromJSON(e)) + : [], winningPrice: isSet(object.winningPrice) ? Coin.fromJSON(object.winningPrice) : undefined, + maxPrice: isSet(object.maxPrice) + ? Coin.fromJSON(object.maxPrice) + : undefined, + numProviders: isSet(object.numProviders) + ? Number(object.numProviders) + : 0, + fundsReleased: isSet(object.fundsReleased) + ? Boolean(object.fundsReleased) + : false, }; }, toJSON(message: Auction): unknown { const obj: any = {}; message.id !== undefined && (obj.id = message.id); + message.kind !== undefined && (obj.kind = message.kind); message.status !== undefined && (obj.status = message.status); message.ownerAddress !== undefined && (obj.ownerAddress = message.ownerAddress); @@ -395,22 +346,37 @@ export const Auction = { (obj.minimumBid = message.minimumBid ? Coin.toJSON(message.minimumBid) : undefined); - message.winnerAddress !== undefined && - (obj.winnerAddress = message.winnerAddress); - message.winningBid !== undefined && - (obj.winningBid = message.winningBid - ? Coin.toJSON(message.winningBid) - : undefined); + if (message.winnerAddresses) { + obj.winnerAddresses = message.winnerAddresses.map((e) => e); + } else { + obj.winnerAddresses = []; + } + if (message.winningBids) { + obj.winningBids = message.winningBids.map((e) => + e ? Coin.toJSON(e) : undefined + ); + } else { + obj.winningBids = []; + } message.winningPrice !== undefined && (obj.winningPrice = message.winningPrice ? Coin.toJSON(message.winningPrice) : undefined); + message.maxPrice !== undefined && + (obj.maxPrice = message.maxPrice + ? Coin.toJSON(message.maxPrice) + : undefined); + message.numProviders !== undefined && + (obj.numProviders = Math.round(message.numProviders)); + message.fundsReleased !== undefined && + (obj.fundsReleased = message.fundsReleased); return obj; }, fromPartial, I>>(object: I): Auction { const message = createBaseAuction(); message.id = object.id ?? ""; + message.kind = object.kind ?? ""; message.status = object.status ?? ""; message.ownerAddress = object.ownerAddress ?? ""; message.createTime = object.createTime ?? undefined; @@ -428,15 +394,19 @@ export const Auction = { object.minimumBid !== undefined && object.minimumBid !== null ? Coin.fromPartial(object.minimumBid) : undefined; - message.winnerAddress = object.winnerAddress ?? ""; - message.winningBid = - object.winningBid !== undefined && object.winningBid !== null - ? Coin.fromPartial(object.winningBid) - : undefined; + message.winnerAddresses = object.winnerAddresses?.map((e) => e) || []; + message.winningBids = + object.winningBids?.map((e) => Coin.fromPartial(e)) || []; message.winningPrice = object.winningPrice !== undefined && object.winningPrice !== null ? Coin.fromPartial(object.winningPrice) : undefined; + message.maxPrice = + object.maxPrice !== undefined && object.maxPrice !== null + ? Coin.fromPartial(object.maxPrice) + : undefined; + message.numProviders = object.numProviders ?? 0; + message.fundsReleased = object.fundsReleased ?? false; return message; }, }; diff --git a/src/proto/cerc/auction/v1/tx.ts b/src/proto/cerc/auction/v1/tx.ts index f3b2c04..f004a74 100644 --- a/src/proto/cerc/auction/v1/tx.ts +++ b/src/proto/cerc/auction/v1/tx.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { Duration } from "../../../google/protobuf/duration"; import { Coin } from "../../../cosmos/base/v1beta1/coin"; -import { Auction, Bid } from "./auction"; +import { Auction, Bid, Params } from "./auction"; import Long from "long"; import _m0 from "protobufjs/minimal"; @@ -9,6 +9,10 @@ export const protobufPackage = "cerc.auction.v1"; /** MsgCreateAuction defines a create auction message */ export interface MsgCreateAuction { + /** Address of the signer */ + signer: string; + /** Auction kind (vickrey | provider) */ + kind: string; /** Duration of the commits phase in seconds */ commitsDuration?: Duration; /** Duration of the reveals phase in seconds */ @@ -17,10 +21,21 @@ export interface MsgCreateAuction { commitFee?: Coin; /** Reveal fees */ revealFee?: Coin; - /** Minimum acceptable bid amount */ + /** + * Minimum acceptable bid amount + * Only applicable in vickrey auctions + */ minimumBid?: Coin; - /** Address of the signer */ - signer: string; + /** + * Maximum acceptable bid amount + * Only applicable in provider auctions + */ + maxPrice?: Coin; + /** + * Number of desired providers (num of auction winners) + * Only applicable in provider auctions + */ + numProviders: number; } /** MsgCreateAuctionResponse returns the details of the created auction */ @@ -61,14 +76,52 @@ export interface MsgRevealBidResponse { auction?: Auction; } +/** MsgUpdateParams is the Msg/UpdateParams request type. */ +export interface MsgUpdateParams { + /** + * authority is the address that controls the module (defaults to x/gov unless + * overwritten). + */ + authority: string; + /** + * params defines the x/auction parameters to update. + * + * NOTE: All parameters must be supplied. + */ + params?: Params; +} + +/** + * MsgUpdateParamsResponse defines the response structure for executing a + * MsgUpdateParams message. + */ +export interface MsgUpdateParamsResponse {} + +/** ReleaseFunds defines the message to pay the winners of provider auctions */ +export interface MsgReleaseFunds { + /** Auction id */ + auctionId: string; + /** Address of the signer */ + signer: string; +} + +/** MsgReleaseFundsResponse returns the state of the auction after releasing the funds */ +export interface MsgReleaseFundsResponse { + /** Auction details */ + auction?: Auction; +} + function createBaseMsgCreateAuction(): MsgCreateAuction { return { + signer: "", + kind: "", commitsDuration: undefined, revealsDuration: undefined, commitFee: undefined, revealFee: undefined, minimumBid: undefined, - signer: "", + maxPrice: undefined, + numProviders: 0, }; } @@ -77,29 +130,38 @@ export const MsgCreateAuction = { message: MsgCreateAuction, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { + if (message.signer !== "") { + writer.uint32(10).string(message.signer); + } + if (message.kind !== "") { + writer.uint32(18).string(message.kind); + } if (message.commitsDuration !== undefined) { Duration.encode( message.commitsDuration, - writer.uint32(10).fork() + writer.uint32(26).fork() ).ldelim(); } if (message.revealsDuration !== undefined) { Duration.encode( message.revealsDuration, - writer.uint32(18).fork() + writer.uint32(34).fork() ).ldelim(); } if (message.commitFee !== undefined) { - Coin.encode(message.commitFee, writer.uint32(26).fork()).ldelim(); + Coin.encode(message.commitFee, writer.uint32(42).fork()).ldelim(); } if (message.revealFee !== undefined) { - Coin.encode(message.revealFee, writer.uint32(34).fork()).ldelim(); + Coin.encode(message.revealFee, writer.uint32(50).fork()).ldelim(); } if (message.minimumBid !== undefined) { - Coin.encode(message.minimumBid, writer.uint32(42).fork()).ldelim(); + Coin.encode(message.minimumBid, writer.uint32(58).fork()).ldelim(); } - if (message.signer !== "") { - writer.uint32(50).string(message.signer); + if (message.maxPrice !== undefined) { + Coin.encode(message.maxPrice, writer.uint32(66).fork()).ldelim(); + } + if (message.numProviders !== 0) { + writer.uint32(72).int32(message.numProviders); } return writer; }, @@ -112,22 +174,31 @@ export const MsgCreateAuction = { const tag = reader.uint32(); switch (tag >>> 3) { case 1: - message.commitsDuration = Duration.decode(reader, reader.uint32()); + message.signer = reader.string(); break; case 2: - message.revealsDuration = Duration.decode(reader, reader.uint32()); + message.kind = reader.string(); break; case 3: - message.commitFee = Coin.decode(reader, reader.uint32()); + message.commitsDuration = Duration.decode(reader, reader.uint32()); break; case 4: - message.revealFee = Coin.decode(reader, reader.uint32()); + message.revealsDuration = Duration.decode(reader, reader.uint32()); break; case 5: - message.minimumBid = Coin.decode(reader, reader.uint32()); + message.commitFee = Coin.decode(reader, reader.uint32()); break; case 6: - message.signer = reader.string(); + message.revealFee = Coin.decode(reader, reader.uint32()); + break; + case 7: + message.minimumBid = Coin.decode(reader, reader.uint32()); + break; + case 8: + message.maxPrice = Coin.decode(reader, reader.uint32()); + break; + case 9: + message.numProviders = reader.int32(); break; default: reader.skipType(tag & 7); @@ -139,6 +210,8 @@ export const MsgCreateAuction = { fromJSON(object: any): MsgCreateAuction { return { + signer: isSet(object.signer) ? String(object.signer) : "", + kind: isSet(object.kind) ? String(object.kind) : "", commitsDuration: isSet(object.commitsDuration) ? Duration.fromJSON(object.commitsDuration) : undefined, @@ -154,12 +227,19 @@ export const MsgCreateAuction = { minimumBid: isSet(object.minimumBid) ? Coin.fromJSON(object.minimumBid) : undefined, - signer: isSet(object.signer) ? String(object.signer) : "", + maxPrice: isSet(object.maxPrice) + ? Coin.fromJSON(object.maxPrice) + : undefined, + numProviders: isSet(object.numProviders) + ? Number(object.numProviders) + : 0, }; }, toJSON(message: MsgCreateAuction): unknown { const obj: any = {}; + message.signer !== undefined && (obj.signer = message.signer); + message.kind !== undefined && (obj.kind = message.kind); message.commitsDuration !== undefined && (obj.commitsDuration = message.commitsDuration ? Duration.toJSON(message.commitsDuration) @@ -180,7 +260,12 @@ export const MsgCreateAuction = { (obj.minimumBid = message.minimumBid ? Coin.toJSON(message.minimumBid) : undefined); - message.signer !== undefined && (obj.signer = message.signer); + message.maxPrice !== undefined && + (obj.maxPrice = message.maxPrice + ? Coin.toJSON(message.maxPrice) + : undefined); + message.numProviders !== undefined && + (obj.numProviders = Math.round(message.numProviders)); return obj; }, @@ -188,6 +273,8 @@ export const MsgCreateAuction = { object: I ): MsgCreateAuction { const message = createBaseMsgCreateAuction(); + message.signer = object.signer ?? ""; + message.kind = object.kind ?? ""; message.commitsDuration = object.commitsDuration !== undefined && object.commitsDuration !== null ? Duration.fromPartial(object.commitsDuration) @@ -208,7 +295,11 @@ export const MsgCreateAuction = { object.minimumBid !== undefined && object.minimumBid !== null ? Coin.fromPartial(object.minimumBid) : undefined; - message.signer = object.signer ?? ""; + message.maxPrice = + object.maxPrice !== undefined && object.maxPrice !== null + ? Coin.fromPartial(object.maxPrice) + : undefined; + message.numProviders = object.numProviders ?? 0; return message; }, }; @@ -548,6 +639,248 @@ export const MsgRevealBidResponse = { }, }; +function createBaseMsgUpdateParams(): MsgUpdateParams { + return { authority: "", params: undefined }; +} + +export const MsgUpdateParams = { + encode( + message: MsgUpdateParams, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.authority !== "") { + writer.uint32(10).string(message.authority); + } + if (message.params !== undefined) { + Params.encode(message.params, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MsgUpdateParams { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgUpdateParams(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.authority = reader.string(); + break; + case 2: + message.params = Params.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): MsgUpdateParams { + return { + authority: isSet(object.authority) ? String(object.authority) : "", + params: isSet(object.params) ? Params.fromJSON(object.params) : undefined, + }; + }, + + toJSON(message: MsgUpdateParams): unknown { + const obj: any = {}; + message.authority !== undefined && (obj.authority = message.authority); + message.params !== undefined && + (obj.params = message.params ? Params.toJSON(message.params) : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): MsgUpdateParams { + const message = createBaseMsgUpdateParams(); + message.authority = object.authority ?? ""; + message.params = + object.params !== undefined && object.params !== null + ? Params.fromPartial(object.params) + : undefined; + return message; + }, +}; + +function createBaseMsgUpdateParamsResponse(): MsgUpdateParamsResponse { + return {}; +} + +export const MsgUpdateParamsResponse = { + encode( + _: MsgUpdateParamsResponse, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number + ): MsgUpdateParamsResponse { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgUpdateParamsResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(_: any): MsgUpdateParamsResponse { + return {}; + }, + + toJSON(_: MsgUpdateParamsResponse): unknown { + const obj: any = {}; + return obj; + }, + + fromPartial, I>>( + _: I + ): MsgUpdateParamsResponse { + const message = createBaseMsgUpdateParamsResponse(); + return message; + }, +}; + +function createBaseMsgReleaseFunds(): MsgReleaseFunds { + return { auctionId: "", signer: "" }; +} + +export const MsgReleaseFunds = { + encode( + message: MsgReleaseFunds, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.auctionId !== "") { + writer.uint32(10).string(message.auctionId); + } + if (message.signer !== "") { + writer.uint32(18).string(message.signer); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MsgReleaseFunds { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgReleaseFunds(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.auctionId = reader.string(); + break; + case 2: + message.signer = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): MsgReleaseFunds { + return { + auctionId: isSet(object.auctionId) ? String(object.auctionId) : "", + signer: isSet(object.signer) ? String(object.signer) : "", + }; + }, + + toJSON(message: MsgReleaseFunds): unknown { + const obj: any = {}; + message.auctionId !== undefined && (obj.auctionId = message.auctionId); + message.signer !== undefined && (obj.signer = message.signer); + return obj; + }, + + fromPartial, I>>( + object: I + ): MsgReleaseFunds { + const message = createBaseMsgReleaseFunds(); + message.auctionId = object.auctionId ?? ""; + message.signer = object.signer ?? ""; + return message; + }, +}; + +function createBaseMsgReleaseFundsResponse(): MsgReleaseFundsResponse { + return { auction: undefined }; +} + +export const MsgReleaseFundsResponse = { + encode( + message: MsgReleaseFundsResponse, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.auction !== undefined) { + Auction.encode(message.auction, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number + ): MsgReleaseFundsResponse { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgReleaseFundsResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.auction = Auction.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): MsgReleaseFundsResponse { + return { + auction: isSet(object.auction) + ? Auction.fromJSON(object.auction) + : undefined, + }; + }, + + toJSON(message: MsgReleaseFundsResponse): unknown { + const obj: any = {}; + message.auction !== undefined && + (obj.auction = message.auction + ? Auction.toJSON(message.auction) + : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): MsgReleaseFundsResponse { + const message = createBaseMsgReleaseFundsResponse(); + message.auction = + object.auction !== undefined && object.auction !== null + ? Auction.fromPartial(object.auction) + : undefined; + return message; + }, +}; + /** Tx defines the gRPC tx interface */ export interface Msg { /** CreateAuction is the command for creating an auction */ @@ -556,6 +889,13 @@ export interface Msg { CommitBid(request: MsgCommitBid): Promise; /** RevealBid is the command for revealing a bid */ RevealBid(request: MsgRevealBid): Promise; + /** + * UpdateParams defines an operation for updating the x/staking module + * parameters. + */ + UpdateParams(request: MsgUpdateParams): Promise; + /** ReleaseFunds is the command for paying the winners of provider auctions */ + ReleaseFunds(request: MsgReleaseFunds): Promise; } export class MsgClientImpl implements Msg { @@ -565,6 +905,8 @@ export class MsgClientImpl implements Msg { this.CreateAuction = this.CreateAuction.bind(this); this.CommitBid = this.CommitBid.bind(this); this.RevealBid = this.RevealBid.bind(this); + this.UpdateParams = this.UpdateParams.bind(this); + this.ReleaseFunds = this.ReleaseFunds.bind(this); } CreateAuction(request: MsgCreateAuction): Promise { const data = MsgCreateAuction.encode(request).finish(); @@ -593,6 +935,30 @@ export class MsgClientImpl implements Msg { MsgRevealBidResponse.decode(new _m0.Reader(data)) ); } + + UpdateParams(request: MsgUpdateParams): Promise { + const data = MsgUpdateParams.encode(request).finish(); + const promise = this.rpc.request( + "cerc.auction.v1.Msg", + "UpdateParams", + data + ); + return promise.then((data) => + MsgUpdateParamsResponse.decode(new _m0.Reader(data)) + ); + } + + ReleaseFunds(request: MsgReleaseFunds): Promise { + const data = MsgReleaseFunds.encode(request).finish(); + const promise = this.rpc.request( + "cerc.auction.v1.Msg", + "ReleaseFunds", + data + ); + return promise.then((data) => + MsgReleaseFundsResponse.decode(new _m0.Reader(data)) + ); + } } interface Rpc { diff --git a/src/proto/cerc/bond/module/v1/module.ts b/src/proto/cerc/bond/module/v1/module.ts index f5faaf0..3e3fc4d 100644 --- a/src/proto/cerc/bond/module/v1/module.ts +++ b/src/proto/cerc/bond/module/v1/module.ts @@ -8,14 +8,26 @@ export const protobufPackage = "cerc.bond.module.v1"; * Module is the app config object of the module. * Learn more: https://docs.cosmos.network/main/building-modules/depinject */ -export interface Module {} +export interface Module { + /** + * authority defines the custom module authority. If not set, defaults to the + * governance module. + */ + authority: string; +} function createBaseModule(): Module { - return {}; + return { authority: "" }; } export const Module = { - encode(_: Module, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + encode( + message: Module, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.authority !== "") { + writer.uint32(18).string(message.authority); + } return writer; }, @@ -26,6 +38,9 @@ export const Module = { while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { + case 2: + message.authority = reader.string(); + break; default: reader.skipType(tag & 7); break; @@ -34,17 +49,21 @@ export const Module = { return message; }, - fromJSON(_: any): Module { - return {}; + fromJSON(object: any): Module { + return { + authority: isSet(object.authority) ? String(object.authority) : "", + }; }, - toJSON(_: Module): unknown { + toJSON(message: Module): unknown { const obj: any = {}; + message.authority !== undefined && (obj.authority = message.authority); return obj; }, - fromPartial, I>>(_: I): Module { + fromPartial, I>>(object: I): Module { const message = createBaseModule(); + message.authority = object.authority ?? ""; return message; }, }; @@ -81,3 +100,7 @@ if (_m0.util.Long !== Long) { _m0.util.Long = Long as any; _m0.configure(); } + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/cerc/bond/v1/tx.ts b/src/proto/cerc/bond/v1/tx.ts index 233653b..de8780a 100644 --- a/src/proto/cerc/bond/v1/tx.ts +++ b/src/proto/cerc/bond/v1/tx.ts @@ -1,4 +1,5 @@ /* eslint-disable */ +import { Params } from "./bond"; import Long from "long"; import { Coin } from "../../../cosmos/base/v1beta1/coin"; import _m0 from "protobufjs/minimal"; @@ -45,6 +46,27 @@ export interface MsgCancelBond { /** MsgCancelBondResponse defines the Msg/CancelBond response type. */ export interface MsgCancelBondResponse {} +/** MsgUpdateParams is the Msg/UpdateParams request type. */ +export interface MsgUpdateParams { + /** + * authority is the address that controls the module (defaults to x/gov unless + * overwritten). + */ + authority: string; + /** + * params defines the x/bond parameters to update. + * + * NOTE: All parameters must be supplied. + */ + params?: Params; +} + +/** + * MsgUpdateParamsResponse defines the response structure for executing a + * MsgUpdateParams message. + */ +export interface MsgUpdateParamsResponse {} + function createBaseMsgCreateBond(): MsgCreateBond { return { signer: "", coins: [] }; } @@ -531,6 +553,120 @@ export const MsgCancelBondResponse = { }, }; +function createBaseMsgUpdateParams(): MsgUpdateParams { + return { authority: "", params: undefined }; +} + +export const MsgUpdateParams = { + encode( + message: MsgUpdateParams, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.authority !== "") { + writer.uint32(10).string(message.authority); + } + if (message.params !== undefined) { + Params.encode(message.params, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MsgUpdateParams { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgUpdateParams(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.authority = reader.string(); + break; + case 2: + message.params = Params.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): MsgUpdateParams { + return { + authority: isSet(object.authority) ? String(object.authority) : "", + params: isSet(object.params) ? Params.fromJSON(object.params) : undefined, + }; + }, + + toJSON(message: MsgUpdateParams): unknown { + const obj: any = {}; + message.authority !== undefined && (obj.authority = message.authority); + message.params !== undefined && + (obj.params = message.params ? Params.toJSON(message.params) : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): MsgUpdateParams { + const message = createBaseMsgUpdateParams(); + message.authority = object.authority ?? ""; + message.params = + object.params !== undefined && object.params !== null + ? Params.fromPartial(object.params) + : undefined; + return message; + }, +}; + +function createBaseMsgUpdateParamsResponse(): MsgUpdateParamsResponse { + return {}; +} + +export const MsgUpdateParamsResponse = { + encode( + _: MsgUpdateParamsResponse, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number + ): MsgUpdateParamsResponse { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgUpdateParamsResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(_: any): MsgUpdateParamsResponse { + return {}; + }, + + toJSON(_: MsgUpdateParamsResponse): unknown { + const obj: any = {}; + return obj; + }, + + fromPartial, I>>( + _: I + ): MsgUpdateParamsResponse { + const message = createBaseMsgUpdateParamsResponse(); + return message; + }, +}; + /** Msg defines the bond Msg service. */ export interface Msg { /** CreateBond defines a method for creating a new bond. */ @@ -541,6 +677,11 @@ export interface Msg { WithdrawBond(request: MsgWithdrawBond): Promise; /** CancelBond defines a method for cancelling a bond. */ CancelBond(request: MsgCancelBond): Promise; + /** + * UpdateParams defines an operation for updating the x/staking module + * parameters. + */ + UpdateParams(request: MsgUpdateParams): Promise; } export class MsgClientImpl implements Msg { @@ -551,6 +692,7 @@ export class MsgClientImpl implements Msg { this.RefillBond = this.RefillBond.bind(this); this.WithdrawBond = this.WithdrawBond.bind(this); this.CancelBond = this.CancelBond.bind(this); + this.UpdateParams = this.UpdateParams.bind(this); } CreateBond(request: MsgCreateBond): Promise { const data = MsgCreateBond.encode(request).finish(); @@ -583,6 +725,14 @@ export class MsgClientImpl implements Msg { MsgCancelBondResponse.decode(new _m0.Reader(data)) ); } + + UpdateParams(request: MsgUpdateParams): Promise { + const data = MsgUpdateParams.encode(request).finish(); + const promise = this.rpc.request("cerc.bond.v1.Msg", "UpdateParams", data); + return promise.then((data) => + MsgUpdateParamsResponse.decode(new _m0.Reader(data)) + ); + } } interface Rpc { diff --git a/src/proto/cerc/registry/module/v1/module.ts b/src/proto/cerc/registry/module/v1/module.ts index 499297f..b4ad606 100644 --- a/src/proto/cerc/registry/module/v1/module.ts +++ b/src/proto/cerc/registry/module/v1/module.ts @@ -8,14 +8,26 @@ export const protobufPackage = "cerc.registry.module.v1"; * Module is the app config object of the module. * Learn more: https://docs.cosmos.network/main/building-modules/depinject */ -export interface Module {} +export interface Module { + /** + * authority defines the custom module authority. If not set, defaults to the + * governance module. + */ + authority: string; +} function createBaseModule(): Module { - return {}; + return { authority: "" }; } export const Module = { - encode(_: Module, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + encode( + message: Module, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.authority !== "") { + writer.uint32(18).string(message.authority); + } return writer; }, @@ -26,6 +38,9 @@ export const Module = { while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { + case 2: + message.authority = reader.string(); + break; default: reader.skipType(tag & 7); break; @@ -34,17 +49,21 @@ export const Module = { return message; }, - fromJSON(_: any): Module { - return {}; + fromJSON(object: any): Module { + return { + authority: isSet(object.authority) ? String(object.authority) : "", + }; }, - toJSON(_: Module): unknown { + toJSON(message: Module): unknown { const obj: any = {}; + message.authority !== undefined && (obj.authority = message.authority); return obj; }, - fromPartial, I>>(_: I): Module { + fromPartial, I>>(object: I): Module { const message = createBaseModule(); + message.authority = object.authority ?? ""; return message; }, }; @@ -81,3 +100,7 @@ if (_m0.util.Long !== Long) { _m0.util.Long = Long as any; _m0.configure(); } + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/cerc/registry/v1/registry.ts b/src/proto/cerc/registry/v1/registry.ts index aa7b004..2ee6f9f 100644 --- a/src/proto/cerc/registry/v1/registry.ts +++ b/src/proto/cerc/registry/v1/registry.ts @@ -1162,7 +1162,7 @@ export const RecordsList = { declare var self: any | undefined; declare var window: any | undefined; declare var global: any | undefined; -var _globalThis: any = (() => { +var globalThis: any = (() => { if (typeof globalThis !== "undefined") return globalThis; if (typeof self !== "undefined") return self; if (typeof window !== "undefined") return window; @@ -1171,10 +1171,10 @@ var _globalThis: any = (() => { })(); function bytesFromBase64(b64: string): Uint8Array { - if (_globalThis.Buffer) { - return Uint8Array.from(_globalThis.Buffer.from(b64, "base64")); + if (globalThis.Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); } else { - const bin = _globalThis.atob(b64); + const bin = globalThis.atob(b64); const arr = new Uint8Array(bin.length); for (let i = 0; i < bin.length; ++i) { arr[i] = bin.charCodeAt(i); @@ -1184,14 +1184,14 @@ function bytesFromBase64(b64: string): Uint8Array { } function base64FromBytes(arr: Uint8Array): string { - if (_globalThis.Buffer) { - return _globalThis.Buffer.from(arr).toString("base64"); + if (globalThis.Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); } else { const bin: string[] = []; arr.forEach((byte) => { bin.push(String.fromCharCode(byte)); }); - return _globalThis.btoa(bin.join("")); + return globalThis.btoa(bin.join("")); } } diff --git a/src/proto/cerc/registry/v1/tx.ts b/src/proto/cerc/registry/v1/tx.ts index afbb40c..9e3d89d 100644 --- a/src/proto/cerc/registry/v1/tx.ts +++ b/src/proto/cerc/registry/v1/tx.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -import { Record, Signature } from "./registry"; +import { Record, Params, Signature } from "./registry"; import Long from "long"; import _m0 from "protobufjs/minimal"; @@ -110,6 +110,27 @@ export interface MsgReassociateRecords { /** MsgReassociateRecordsResponse */ export interface MsgReassociateRecordsResponse {} +/** MsgUpdateParams is the Msg/UpdateParams request type. */ +export interface MsgUpdateParams { + /** + * authority is the address that controls the module (defaults to x/gov unless + * overwritten). + */ + authority: string; + /** + * params defines the x/registry parameters to update. + * + * NOTE: All parameters must be supplied. + */ + params?: Params; +} + +/** + * MsgUpdateParamsResponse defines the response structure for executing a + * MsgUpdateParams message. + */ +export interface MsgUpdateParamsResponse {} + function createBaseMsgSetRecord(): MsgSetRecord { return { bondId: "", signer: "", payload: undefined }; } @@ -1359,6 +1380,120 @@ export const MsgReassociateRecordsResponse = { }, }; +function createBaseMsgUpdateParams(): MsgUpdateParams { + return { authority: "", params: undefined }; +} + +export const MsgUpdateParams = { + encode( + message: MsgUpdateParams, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.authority !== "") { + writer.uint32(10).string(message.authority); + } + if (message.params !== undefined) { + Params.encode(message.params, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MsgUpdateParams { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgUpdateParams(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.authority = reader.string(); + break; + case 2: + message.params = Params.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): MsgUpdateParams { + return { + authority: isSet(object.authority) ? String(object.authority) : "", + params: isSet(object.params) ? Params.fromJSON(object.params) : undefined, + }; + }, + + toJSON(message: MsgUpdateParams): unknown { + const obj: any = {}; + message.authority !== undefined && (obj.authority = message.authority); + message.params !== undefined && + (obj.params = message.params ? Params.toJSON(message.params) : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): MsgUpdateParams { + const message = createBaseMsgUpdateParams(); + message.authority = object.authority ?? ""; + message.params = + object.params !== undefined && object.params !== null + ? Params.fromPartial(object.params) + : undefined; + return message; + }, +}; + +function createBaseMsgUpdateParamsResponse(): MsgUpdateParamsResponse { + return {}; +} + +export const MsgUpdateParamsResponse = { + encode( + _: MsgUpdateParamsResponse, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number + ): MsgUpdateParamsResponse { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgUpdateParamsResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(_: any): MsgUpdateParamsResponse { + return {}; + }, + + toJSON(_: MsgUpdateParamsResponse): unknown { + const obj: any = {}; + return obj; + }, + + fromPartial, I>>( + _: I + ): MsgUpdateParamsResponse { + const message = createBaseMsgUpdateParamsResponse(); + return message; + }, +}; + /** Msg is a service which exposes the registry functionality */ export interface Msg { /** SetRecord records a new record with given payload and bond id */ @@ -1391,6 +1526,11 @@ export interface Msg { SetAuthorityBond( request: MsgSetAuthorityBond ): Promise; + /** + * UpdateParams defines an operation for updating the x/staking module + * parameters. + */ + UpdateParams(request: MsgUpdateParams): Promise; } export class MsgClientImpl implements Msg { @@ -1407,6 +1547,7 @@ export class MsgClientImpl implements Msg { this.DeleteName = this.DeleteName.bind(this); this.ReserveAuthority = this.ReserveAuthority.bind(this); this.SetAuthorityBond = this.SetAuthorityBond.bind(this); + this.UpdateParams = this.UpdateParams.bind(this); } SetRecord(request: MsgSetRecord): Promise { const data = MsgSetRecord.encode(request).finish(); @@ -1529,6 +1670,18 @@ export class MsgClientImpl implements Msg { MsgSetAuthorityBondResponse.decode(new _m0.Reader(data)) ); } + + UpdateParams(request: MsgUpdateParams): Promise { + const data = MsgUpdateParams.encode(request).finish(); + const promise = this.rpc.request( + "cerc.registry.v1.Msg", + "UpdateParams", + data + ); + return promise.then((data) => + MsgUpdateParamsResponse.decode(new _m0.Reader(data)) + ); + } } interface Rpc { diff --git a/src/proto/cosmos/base/query/v1beta1/pagination.ts b/src/proto/cosmos/base/query/v1beta1/pagination.ts index 34713f7..fe65b07 100644 --- a/src/proto/cosmos/base/query/v1beta1/pagination.ts +++ b/src/proto/cosmos/base/query/v1beta1/pagination.ts @@ -251,7 +251,7 @@ export const PageResponse = { declare var self: any | undefined; declare var window: any | undefined; declare var global: any | undefined; -var _globalThis: any = (() => { +var globalThis: any = (() => { if (typeof globalThis !== "undefined") return globalThis; if (typeof self !== "undefined") return self; if (typeof window !== "undefined") return window; @@ -260,10 +260,10 @@ var _globalThis: any = (() => { })(); function bytesFromBase64(b64: string): Uint8Array { - if (_globalThis.Buffer) { - return Uint8Array.from(_globalThis.Buffer.from(b64, "base64")); + if (globalThis.Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); } else { - const bin = _globalThis.atob(b64); + const bin = globalThis.atob(b64); const arr = new Uint8Array(bin.length); for (let i = 0; i < bin.length; ++i) { arr[i] = bin.charCodeAt(i); @@ -273,14 +273,14 @@ function bytesFromBase64(b64: string): Uint8Array { } function base64FromBytes(arr: Uint8Array): string { - if (_globalThis.Buffer) { - return _globalThis.Buffer.from(arr).toString("base64"); + if (globalThis.Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); } else { const bin: string[] = []; arr.forEach((byte) => { bin.push(String.fromCharCode(byte)); }); - return _globalThis.btoa(bin.join("")); + return globalThis.btoa(bin.join("")); } } diff --git a/src/proto/google/protobuf/descriptor.ts b/src/proto/google/protobuf/descriptor.ts index e024050..e1cda94 100644 --- a/src/proto/google/protobuf/descriptor.ts +++ b/src/proto/google/protobuf/descriptor.ts @@ -4324,7 +4324,7 @@ export const GeneratedCodeInfo_Annotation = { declare var self: any | undefined; declare var window: any | undefined; declare var global: any | undefined; -var _globalThis: any = (() => { +var globalThis: any = (() => { if (typeof globalThis !== "undefined") return globalThis; if (typeof self !== "undefined") return self; if (typeof window !== "undefined") return window; @@ -4333,10 +4333,10 @@ var _globalThis: any = (() => { })(); function bytesFromBase64(b64: string): Uint8Array { - if (_globalThis.Buffer) { - return Uint8Array.from(_globalThis.Buffer.from(b64, "base64")); + if (globalThis.Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); } else { - const bin = _globalThis.atob(b64); + const bin = globalThis.atob(b64); const arr = new Uint8Array(bin.length); for (let i = 0; i < bin.length; ++i) { arr[i] = bin.charCodeAt(i); @@ -4346,14 +4346,14 @@ function bytesFromBase64(b64: string): Uint8Array { } function base64FromBytes(arr: Uint8Array): string { - if (_globalThis.Buffer) { - return _globalThis.Buffer.from(arr).toString("base64"); + if (globalThis.Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); } else { const bin: string[] = []; arr.forEach((byte) => { bin.push(String.fromCharCode(byte)); }); - return _globalThis.btoa(bin.join("")); + return globalThis.btoa(bin.join("")); } } diff --git a/src/registry-client.ts b/src/registry-client.ts index a1cdde1..4ab41ad 100644 --- a/src/registry-client.ts +++ b/src/registry-client.ts @@ -44,6 +44,7 @@ const historyFields = ` const auctionFields = ` id + kind status ownerAddress createTime @@ -61,8 +62,8 @@ const auctionFields = ` type quantity } - winnerAddress - winnerBid { + winnerAddresses + winnerBids { type quantity } @@ -70,6 +71,12 @@ const auctionFields = ` type quantity } + maxPrice { + type + quantity + } + numProviders + fundsReleased bids { bidderAddress status diff --git a/src/testing/helper.ts b/src/testing/helper.ts index 8324402..f3d5c81 100644 --- a/src/testing/helper.ts +++ b/src/testing/helper.ts @@ -2,7 +2,7 @@ import assert from 'assert'; import yaml from 'node-yaml'; import semver from 'semver'; -import { Account } from '../index'; +import { Account, Registry } from '../index'; const DEFAULT_CHAIN_ID = 'laconic_9000-1'; @@ -30,8 +30,8 @@ export const getConfig = () => { rpcEndpoint: process.env.LACONICD_RPC_ENDPOINT || 'http://localhost:26657', gqlEndpoint: process.env.LACONICD_GQL_ENDPOINT || 'http://localhost:9473/api', fee: { - amount: [{ denom: 'alnt', amount: '400000' }], - gas: '400000' + amount: [{ denom: 'alnt', amount: '200000' }], + gas: '200000' } }; }; diff --git a/src/types/cerc/auction/message.ts b/src/types/cerc/auction/message.ts index 178798a..4329e9e 100644 --- a/src/types/cerc/auction/message.ts +++ b/src/types/cerc/auction/message.ts @@ -1,19 +1,32 @@ import { EncodeObject, GeneratedType } from '@cosmjs/proto-signing'; -import { MsgCommitBidResponse, MsgCommitBid, MsgRevealBid, MsgRevealBidResponse } from '../../../proto/cerc/auction/v1/tx'; +import { MsgCommitBidResponse, MsgCommitBid, MsgRevealBid, MsgRevealBidResponse, MsgCreateAuction, MsgCreateAuctionResponse, MsgReleaseFunds, MsgReleaseFundsResponse } from '../../../proto/cerc/auction/v1/tx'; +export const typeUrlMsgCreateAuction = '/cerc.auction.v1.MsgCreateAuction'; export const typeUrlMsgCommitBid = '/cerc.auction.v1.MsgCommitBid'; export const typeUrlMsgCommitBidResponse = '/cerc.auction.v1.MsgCommitBidResponse'; export const typeUrlMsgRevealBid = '/cerc.auction.v1.MsgRevealBid'; export const typeUrlMsgRevealBidResponse = '/cerc.auction.v1.MsgRevealBidResponse'; +export const typeUrlMsgCreateAuctionResponse = '/cerc.auction.v1.MsgCreateAuctionResponse'; +export const typeUrlMsgReleaseFunds = '/cerc.auction.v1.MsgReleaseFunds'; +export const typeUrlMsgReleaseFundsResponse = '/cerc.auction.v1.MsgReleaseFundsResponse'; export const auctionTypes: ReadonlyArray<[string, GeneratedType]> = [ + [typeUrlMsgCreateAuction, MsgCreateAuction], [typeUrlMsgCommitBid, MsgCommitBid], [typeUrlMsgCommitBidResponse, MsgCommitBidResponse], [typeUrlMsgRevealBid, MsgRevealBid], - [typeUrlMsgRevealBidResponse, MsgRevealBidResponse] + [typeUrlMsgRevealBidResponse, MsgRevealBidResponse], + [typeUrlMsgCreateAuctionResponse, MsgCreateAuctionResponse], + [typeUrlMsgReleaseFunds, MsgReleaseFunds], + [typeUrlMsgReleaseFundsResponse, MsgReleaseFundsResponse] ]; +export interface MsgCreateAuctionEncodeObject extends EncodeObject { + readonly typeUrl: '/cerc.auction.v1.MsgCreateAuction'; + readonly value: Partial; +} + export interface MsgCommitBidEncodeObject extends EncodeObject { readonly typeUrl: '/cerc.auction.v1.MsgCommitBid'; readonly value: Partial; @@ -24,6 +37,16 @@ export interface MsgRevealBidEncodeObject extends EncodeObject { readonly value: Partial; } +export interface MsgReleaseFundsEncodeObject extends EncodeObject { + readonly typeUrl: '/cerc.auction.v1.MsgReleaseFunds'; + readonly value: Partial; +} + +export const INVALID_BID_ERROR = 'Bid is higher than max price'; +export const RELEASE_FUNDS_ERROR = 'Auction funds already released'; +export const OWNER_MISMATCH_ERROR = 'Only auction owner can release funds'; +export const AUCTION_ERRORS = [INVALID_BID_ERROR, RELEASE_FUNDS_ERROR, OWNER_MISMATCH_ERROR]; + export interface MessageMsgCommitBid { auctionId: string, commitHash: string, @@ -33,3 +56,26 @@ export interface MessageMsgRevealBid { auctionId: string, reveal: string, } + +export interface MessageCreateVickreyAuction { + commitsDuration: string; + revealsDuration: string; + denom: string; + commitFee: string; + revealFee: string; + minimumBid: string; +} + +export interface MessageCreateProviderAuction { + commitsDuration: string; + revealsDuration: string; + denom: string; + commitFee: string; + revealFee: string; + maxPrice: string; + numProviders: number; +} + +export interface MessageMsgReleaseFunds { + auctionId: string +}