diff --git a/src/bond.test.ts b/src/bond.test.ts index 1c4e700..77369c4 100644 --- a/src/bond.test.ts +++ b/src/bond.test.ts @@ -1,5 +1,9 @@ +import path from 'path'; + import { Registry } from './index'; -import { getConfig } from './testing/helper'; +import { ensureUpdatedConfig, getConfig } from './testing/helper'; + +const WATCHER_YML_PATH = path.join(__dirname, './testing/data/watcher.yml'); const { chainId, restEndpoint, gqlEndpoint, privateKey, fee } = getConfig(); @@ -7,10 +11,23 @@ jest.setTimeout(90 * 1000); const bondTests = () => { let registry: Registry; + + let watcher: any; + + let version1: string; + let version2: string; + let bondId1: string; + let bondId2: string; let bondOwner: string; + const publishNewWatcherVersion = async (bondId: string) => { + watcher = await ensureUpdatedConfig(WATCHER_YML_PATH); + await registry.setRecord({ privateKey, record: watcher.record, bondId }, privateKey, fee); + return watcher.record.version; + }; + beforeAll(async () => { registry = new Registry(restEndpoint, gqlEndpoint, chainId); }); @@ -72,6 +89,60 @@ const bondTests = () => { expect(bond.owner).toBe(""); expect(bond.balance).toHaveLength(0); }); + + test('Associate/Dissociate bond.', async () => { + bondId1 = await registry.getNextBondId(privateKey); + expect(bondId1).toBeDefined(); + await registry.createBond({ denom: 'aphoton', amount: '1000000000' }, privateKey, fee); + + // Create a new record. + version1 = await publishNewWatcherVersion(bondId1); + let [record1] = await registry.queryRecords({ type: watcher.record.type, name: watcher.record.name, version: version1 }, true); + expect(record1.bondId).toBe(bondId1); + + // Dissociate record, query and confirm. + await registry.dissociateBond({ recordId: record1.id }, privateKey, fee); + [record1] = await registry.queryRecords({ type: watcher.record.type, name: watcher.record.name, version: version1 }, true); + expect(record1.bondId).toBe(''); + + // Associate record with bond, query and confirm. + await registry.associateBond({ recordId: record1.id, bondId: bondId1 }, privateKey, fee); + [record1] = await registry.queryRecords({ type: watcher.record.type, name: watcher.record.name, version: version1 }, true); + expect(record1.bondId).toBe(bondId1); + }); + + test('Reassociate/Dissociate records.', async () => { + // Create a new record version. + version2 = await publishNewWatcherVersion(bondId1); + + // Check version1, version2 as associated with bondId1. + let records; + records = await registry.queryRecords({ type: watcher.record.type, name: watcher.record.name, version: version1 }, true); + expect(records[0].bondId).toBe(bondId1); + records = await registry.queryRecords({ type: watcher.record.type, name: watcher.record.name, version: version2 }, true); + expect(records[0].bondId).toBe(bondId1); + + // Create another bond. + bondId2 = await registry.getNextBondId(privateKey); + expect(bondId2).toBeDefined(); + await registry.createBond({ denom: 'aphoton', amount: '1000000000' }, privateKey, fee); + const [bond] = await registry.getBondsByIds([bondId2]); + expect(bond.id).toBe(bondId2); + + // Reassociate records from bondId1 to bondId2, verify change. + await registry.reassociateRecords({ oldBondId: bondId1, newBondId: bondId2 }, privateKey, fee); + records = await registry.queryRecords({ type: watcher.record.type, name: watcher.record.name, version: version1 }, true); + expect(records[0].bondId).toBe(bondId2); + records = await registry.queryRecords({ type: watcher.record.type, name: watcher.record.name, version: version2 }, true); + expect(records[0].bondId).toBe(bondId2); + + // Dissociate all records from bond, verify change. + await registry.dissociateRecords({ bondId: bondId2 }, privateKey, fee); + records = await registry.queryRecords({ type: watcher.record.type, name: watcher.record.name, version: version1 }, true); + expect(records[0].bondId).toBe(''); + records = await registry.queryRecords({ type: watcher.record.type, name: watcher.record.name, version: version2 }, true); + expect(records[0].bondId).toBe(''); + }); }; describe('Bonds', bondTests); diff --git a/src/index.ts b/src/index.ts index c7862fc..7805f36 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,12 +15,20 @@ import { createTransaction } from "./txbuilder"; import { Payload, Record } from './types'; import { Util } from './util'; import { + createTxMsgAssociateBond, createTxMsgCancelBond, createTxMsgCreateBond, + createTxMsgDissociateBond, + createTxMsgDissociateRecords, + createTxMsgReAssociateRecords, createTxMsgRefillBond, createTxMsgWithdrawBond, + MessageMsgAssociateBond, MessageMsgCancelBond, MessageMsgCreateBond, + MessageMsgDissociateBond, + MessageMsgDissociateRecords, + MessageMsgReAssociateRecords, MessageMsgRefillBond, MessageMsgWithdrawBond } from "./messages/bond"; @@ -104,6 +112,10 @@ export class Registry { // https://gist.github.com/nikugogoi/de55d390574ded3466abad8bffd81952#file-txresponse-js-L7 const errorMessage = NAMESERVICE_ERRORS.find(message => error.includes(message)) + if (!errorMessage) { + console.error(error) + } + return errorMessage || DEFAULT_WRITE_ERROR; } @@ -290,6 +302,62 @@ export class Registry { return parseTxResponse(result); } + /** + * Associate record with bond. + */ + async associateBond(params: MessageMsgAssociateBond, privateKey: string, fee: Fee) { + let result; + const account = new Account(Buffer.from(privateKey, 'hex')); + const sender = await this._getSender(account); + + const msg = createTxMsgAssociateBond(this._chain, sender, fee, '', params) + result = await this._submitTx(msg, privateKey, sender); + + return parseTxResponse(result); + } + + /** + * Dissociate record from bond. + */ + async dissociateBond(params: MessageMsgDissociateBond, privateKey: string, fee: Fee) { + let result; + const account = new Account(Buffer.from(privateKey, 'hex')); + const sender = await this._getSender(account); + + const msg = createTxMsgDissociateBond(this._chain, sender, fee, '', params) + result = await this._submitTx(msg, privateKey, sender); + + return parseTxResponse(result); + } + + /** + * Dissociate all records from bond. + */ + async dissociateRecords(params: MessageMsgDissociateRecords, privateKey: string, fee: Fee) { + let result; + const account = new Account(Buffer.from(privateKey, 'hex')); + const sender = await this._getSender(account); + + const msg = createTxMsgDissociateRecords(this._chain, sender, fee, '', params) + result = await this._submitTx(msg, privateKey, sender); + + return parseTxResponse(result); + } + + /** + * Reassociate records (switch bond). + */ + async reassociateRecords(params: MessageMsgReAssociateRecords, privateKey: string, fee: Fee) { + let result; + const account = new Account(Buffer.from(privateKey, 'hex')); + const sender = await this._getSender(account); + + const msg = createTxMsgReAssociateRecords(this._chain, sender, fee, '', params) + result = await this._submitTx(msg, privateKey, sender); + + return parseTxResponse(result); + } + /** * Reserve authority. */ @@ -425,6 +493,7 @@ export class Registry { await recordSignerAccount.signPayload(payload); // Send record payload Tx. + txPrivateKey = txPrivateKey || recordSignerAccount.getPrivateKey(); return this._submitRecordPayloadTx({ payload, bondId }, txPrivateKey, fee); } diff --git a/src/messages/bond.ts b/src/messages/bond.ts index b043959..a8e004f 100644 --- a/src/messages/bond.ts +++ b/src/messages/bond.ts @@ -8,6 +8,7 @@ import { } from '@tharsis/transactions' import * as bondTx from '../proto/vulcanize/bond/v1beta1/tx' +import * as nameserviceTx from '../proto/vulcanize/nameservice/v1beta1/tx' import * as coin from '../proto/cosmos/base/v1beta1/coin' import { createTx } from './util' @@ -53,6 +54,36 @@ const MSG_CANCEL_BOND_TYPES = { ] } +const MSG_ASSOCIATE_BOND_TYPES = { + MsgValue: [ + { name: 'record_id', type: 'string' }, + { name: 'bond_id', type: 'string' }, + { name: 'signer', type: 'string' }, + ] +} + +const MSG_DISSOCIATE_BOND_TYPES = { + MsgValue: [ + { name: 'record_id', type: 'string' }, + { name: 'signer', type: 'string' }, + ] +} + +const MSG_DISSOCIATE_RECORDS_TYPES = { + MsgValue: [ + { name: 'bond_id', type: 'string' }, + { name: 'signer', type: 'string' }, + ] +} + +const MSG_REASSOCIATE_RECORDS_TYPES = { + MsgValue: [ + { name: 'new_bond_id', type: 'string' }, + { name: 'old_bond_id', type: 'string' }, + { name: 'signer', type: 'string' }, + ] +} + export interface MessageMsgCreateBond { amount: string denom: string @@ -74,6 +105,24 @@ export interface MessageMsgCancelBond { id: string } +export interface MessageMsgAssociateBond { + bondId: string, + recordId: string +} + +export interface MessageMsgDissociateBond { + recordId: string +} + +export interface MessageMsgDissociateRecords { + bondId: string +} + +export interface MessageMsgReAssociateRecords { + newBondId: string + oldBondId: string +} + export function createTxMsgCreateBond( chain: Chain, sender: Sender, @@ -172,6 +221,98 @@ export function createTxMsgCancelBond( return createTx(chain, sender, fee, memo, types, msg, msgCosmos) } +export function createTxMsgAssociateBond( + chain: Chain, + sender: Sender, + fee: Fee, + memo: string, + params: MessageMsgAssociateBond, +) { + const types = generateTypes(MSG_ASSOCIATE_BOND_TYPES) + + const msg = createMsgAssociateBond( + params.recordId, + params.bondId, + sender.accountAddress + ) + + const msgCosmos = protoCreateMsgAssociateBond( + params.recordId, + params.bondId, + sender.accountAddress + ) + + return createTx(chain, sender, fee, memo, types, msg, msgCosmos) +} + +export function createTxMsgDissociateBond( + chain: Chain, + sender: Sender, + fee: Fee, + memo: string, + params: MessageMsgDissociateBond, +) { + const types = generateTypes(MSG_DISSOCIATE_BOND_TYPES) + + const msg = createMsgDissociateBond( + params.recordId, + sender.accountAddress + ) + + const msgCosmos = protoCreateMsgDissociateBond( + params.recordId, + sender.accountAddress + ) + + return createTx(chain, sender, fee, memo, types, msg, msgCosmos) +} + +export function createTxMsgDissociateRecords( + chain: Chain, + sender: Sender, + fee: Fee, + memo: string, + params: MessageMsgDissociateRecords, +) { + const types = generateTypes(MSG_DISSOCIATE_RECORDS_TYPES) + + const msg = createMsgDissociateRecords( + params.bondId, + sender.accountAddress + ) + + const msgCosmos = protoCreateMsgDissociateRecords( + params.bondId, + sender.accountAddress + ) + + return createTx(chain, sender, fee, memo, types, msg, msgCosmos) +} + +export function createTxMsgReAssociateRecords( + chain: Chain, + sender: Sender, + fee: Fee, + memo: string, + params: MessageMsgReAssociateRecords, +) { + const types = generateTypes(MSG_REASSOCIATE_RECORDS_TYPES) + + const msg = createMsgReAssociateRecords( + params.newBondId, + params.oldBondId, + sender.accountAddress + ) + + const msgCosmos = protoCreateMsgReAssociateRecords( + params.newBondId, + params.oldBondId, + sender.accountAddress + ) + + return createTx(chain, sender, fee, memo, types, msg, msgCosmos) +} + function createMsgCreateBond( signer: string, amount: string, @@ -327,3 +468,123 @@ const protoCreateMsgCancelBond = ( path: 'vulcanize.bond.v1beta1.MsgCancelBond', } } + +function createMsgAssociateBond( + recordId: string, + bondId: string, + signer: string +) { + return { + type: 'nameservice/AssociateBond', + value: { + record_id: recordId, + bond_id: bondId, + signer + }, + } +} + +const protoCreateMsgAssociateBond = ( + recordId: string, + bondId: string, + signer: string +) => { + const associateBondMessage = new nameserviceTx.vulcanize.nameservice.v1beta1.MsgAssociateBond({ + record_id: recordId, + bond_id: bondId, + signer + }) + + return { + message: associateBondMessage, + path: 'vulcanize.nameservice.v1beta1.MsgAssociateBond', + } +} + +function createMsgDissociateBond( + recordId: string, + signer: string +) { + return { + type: 'nameservice/DissociateBond', + value: { + record_id: recordId, + signer + }, + } +} + +const protoCreateMsgDissociateBond = ( + recordId: string, + signer: string +) => { + const dissociateBondMessage = new nameserviceTx.vulcanize.nameservice.v1beta1.MsgDissociateBond({ + record_id: recordId, + signer + }) + + return { + message: dissociateBondMessage, + path: 'vulcanize.nameservice.v1beta1.MsgDissociateBond', + } +} + +function createMsgDissociateRecords( + bondId: string, + signer: string +) { + return { + type: 'nameservice/DissociateRecords', + value: { + bond_id: bondId, + signer + }, + } +} + +const protoCreateMsgDissociateRecords = ( + bondId: string, + signer: string +) => { + const dissociateRecordsMessage = new nameserviceTx.vulcanize.nameservice.v1beta1.MsgDissociateRecords({ + bond_id: bondId, + signer + }) + + return { + message: dissociateRecordsMessage, + path: 'vulcanize.nameservice.v1beta1.MsgDissociateRecords', + } +} + +function createMsgReAssociateRecords( + newBondId: string, + oldBondId: string, + signer: string +) { + return { + type: 'nameservice/ReassociateRecords', + value: { + new_bond_id: newBondId, + old_bond_id: oldBondId, + signer + }, + } +} + +const protoCreateMsgReAssociateRecords = ( + newBondId: string, + oldBondId: string, + signer: string +) => { + const reAssociateRecordsMessage = new nameserviceTx.vulcanize.nameservice.v1beta1.MsgReAssociateRecords({ + new_bond_id: newBondId, + old_bond_id: oldBondId, + signer + }) + + return { + message: reAssociateRecordsMessage, + path: 'vulcanize.nameservice.v1beta1.MsgReAssociateRecords', + } +} diff --git a/src/sdk.test.ts b/src/sdk.test.ts index cc31e43..0380d86 100644 --- a/src/sdk.test.ts +++ b/src/sdk.test.ts @@ -33,7 +33,7 @@ describe('Querying', () => { expect(registry.chainID).toBe(chainId); }); - xtest('Get status.', async () => { + test('Get status.', async () => { const status = await registry.getStatus(); expect(status).toBeDefined(); expect(status.version).toBeDefined(); diff --git a/src/testing/helper.ts b/src/testing/helper.ts index f5f44aa..15ba347 100644 --- a/src/testing/helper.ts +++ b/src/testing/helper.ts @@ -26,7 +26,7 @@ export const getBaseConfig = async (path: string) => { export const provisionBondId = async (registry: Registry, privateKey: string, fee: Fee) => { let bonds = await registry.queryBonds(); if (!bonds.length) { - await registry.createBond({ denom: 'uwire', amount: '1000000000' }, privateKey, fee); + await registry.createBond({ denom: 'aphoton', amount: '1000000000' }, privateKey, fee); bonds = await registry.queryBonds(); }