├── src ├── core │ ├── gov │ │ ├── proposals │ │ │ ├── index.ts │ │ │ └── TextProposal.ts │ │ ├── msgs │ │ │ ├── index.ts │ │ │ ├── MsgDeposit.spec.ts │ │ │ ├── MsgSubmitProposal.spec.ts │ │ │ ├── MsgVote.ts │ │ │ ├── MsgDeposit.ts │ │ │ ├── MsgVoteWeighted.ts │ │ │ └── MsgSubmitProposal.ts │ │ └── Vote.ts │ ├── params │ │ └── proposals │ │ │ ├── index.ts │ │ │ └── ParameterChangeProposal.ts │ ├── distribution │ │ ├── proposals │ │ │ ├── index.ts │ │ │ └── CommunityPoolSpendProposal.ts │ │ ├── msgs │ │ │ ├── MsgModifyWithdrawAddress.spec.ts │ │ │ ├── MsgWithdrawDelegationReward.spec.ts │ │ │ ├── MsgWithdrawValidatorCommission.spec.ts │ │ │ ├── index.ts │ │ │ ├── MsgFundCommunityPool.ts │ │ │ ├── MsgWithdrawValidatorCommission.ts │ │ │ ├── MsgModifyWithdrawAddress.ts │ │ │ └── MsgWithdrawDelegationReward.ts │ │ └── params.ts │ ├── oracle │ │ ├── AggregateExchangeRatePrevote.data.json │ │ ├── AggregateExchangeRateVote.spec.ts │ │ ├── AggregateExchangeRatePrevote.spec.ts │ │ ├── msgs │ │ │ ├── MsgDelegateFeedConsent.spec.ts │ │ │ ├── MsgAggregateExchangeRateVote.spec.ts │ │ │ ├── index.ts │ │ │ ├── MsgAggregateExchangeRatePrevote.ts │ │ │ └── MsgDelegateFeedConsent.ts │ │ ├── AggregateExchangeRatePrevote.ts │ │ ├── AggregateExchangeRateVote.ts │ │ ├── AggregateExchangeRateVote.data.json │ │ └── params.ts │ ├── slashing │ │ ├── msgs │ │ │ ├── index.ts │ │ │ ├── MsgUnjail.spec.ts │ │ │ └── MsgUnjail.ts │ │ └── params.ts │ ├── StdTx.spec.ts │ ├── msgauth │ │ └── msgs │ │ │ ├── MsgRevokeAuthorization.data.json │ │ │ ├── MsgExecAuthorized.spec.ts │ │ │ ├── MsgGrantAuthorization.spec.ts │ │ │ ├── MsgRevokeAuthorization.spec.ts │ │ │ ├── index.ts │ │ │ ├── MsgExecAuthorized.data.json │ │ │ ├── MsgExecAuthorized.ts │ │ │ ├── MsgGrantAuthorization.data.json │ │ │ ├── MsgRevokeAuthorization.ts │ │ │ └── MsgGrantAuthorization.ts │ ├── bank │ │ └── msgs │ │ │ ├── index.ts │ │ │ ├── MsgSend.spec.ts │ │ │ └── MsgSend.ts │ ├── market │ │ ├── msgs │ │ │ ├── index.ts │ │ │ ├── MsgSwap.spec.ts │ │ │ ├── MsgSwap.ts │ │ │ └── MsgSwapSend.ts │ │ └── params.ts │ ├── ValidatorSet.ts │ ├── auth │ │ ├── index.ts │ │ ├── LazyGradedVestingAccount.data.json │ │ ├── Account.ts │ │ ├── Account.data.json │ │ ├── LazyGradedVestingAccount.spec.ts │ │ └── Account.spec.ts │ ├── staking │ │ ├── Delegation.spec.ts │ │ ├── Redelegation.spec.ts │ │ ├── UnbondingDelegation.spec.ts │ │ ├── msgs │ │ │ ├── MsgDelegate.spec.ts │ │ │ ├── MsgUndelegate.spec.ts │ │ │ ├── MsgEditValidator.spec.ts │ │ │ ├── MsgBeginRedelegate.spec.ts │ │ │ ├── MsgCreateValidator.spec.ts │ │ │ ├── index.ts │ │ │ ├── MsgDelegate.ts │ │ │ ├── MsgUndelegate.ts │ │ │ ├── MsgBeginRedelegate.ts │ │ │ ├── MsgEditValidator.ts │ │ │ └── MsgCreateValidator.ts │ │ ├── Delegation.data.json │ │ ├── params.ts │ │ ├── Delegation.ts │ │ ├── Redelegation.data.json │ │ └── Validator.spec.ts │ ├── Denom.ts │ ├── wasm │ │ ├── msgs │ │ │ ├── MsgExecuteContract.spec.ts │ │ │ ├── MsgStoreCode.ts │ │ │ ├── MsgClearContractAdmin.ts │ │ │ ├── index.ts │ │ │ ├── MsgUpdateContractAdmin.ts │ │ │ ├── MsgMigrateCode.ts │ │ │ ├── MsgMigrateContract.ts │ │ │ ├── MsgExecuteContract.ts │ │ │ └── MsgInstantiateContract.ts │ │ └── params.ts │ ├── Int.spec.ts │ ├── StdSignature.ts │ ├── treasury │ │ ├── PolicyConstraints.spec.ts │ │ ├── params.ts │ │ └── PolicyConstraints.ts │ ├── StdFee.ts │ ├── Deposit.ts │ ├── Block.ts │ ├── PublicKey.ts │ ├── index.ts │ ├── mint │ │ └── params.ts │ ├── StdTx.ts │ └── StdSignMsg.ts ├── util │ ├── index.ts │ ├── convert.ts │ ├── hash.ts │ ├── json.spec.ts │ ├── json.ts │ ├── hash.spec.ts │ └── contract.ts ├── key │ ├── index.ts │ ├── RawKey.ts │ └── MnemonicKey.ts ├── index.ts ├── client │ ├── lcd │ │ ├── api │ │ │ ├── BaseAPI.ts │ │ │ ├── index.ts │ │ │ ├── WasmAPI.spec.ts │ │ │ ├── MarketAPI.spec.ts │ │ │ ├── DistributionAPI.spec.ts │ │ │ ├── SlashingAPI.spec.ts │ │ │ ├── BankAPI.spec.ts │ │ │ ├── GovAPI.spec.ts │ │ │ ├── TreasuryAPI.spec.ts │ │ │ ├── SupplyAPI.ts │ │ │ ├── BankAPI.ts │ │ │ ├── OracleAPI.spec.ts │ │ │ ├── MintAPI.spec.ts │ │ │ ├── AuthAPI.ts │ │ │ ├── MsgAuthAPI.ts │ │ │ ├── AuthAPI.spec.ts │ │ │ ├── TendermintAPI.ts │ │ │ ├── MintAPI.ts │ │ │ ├── MarketAPI.ts │ │ │ └── WasmAPI.ts │ │ ├── LCDClient.spec.ts │ │ ├── LCDUtils.spec.ts │ │ ├── Wallet.spec.ts │ │ ├── APIRequester.ts │ │ ├── Wallet.ts │ │ └── LCDUtils.ts │ └── index.ts └── extension │ └── PostMessageStream.ts ├── img └── terrajs-diagram.png ├── integration-tests ├── contract.wasm ├── validatorsWithVotingPower.ts ├── mnemonickey.ts ├── fromContractEvents.ts ├── clikey.ts ├── websocket.ts ├── submitProposal.ts ├── send.ts ├── executeContract.ts ├── estimateFee.ts ├── contract.ts └── paramchangeproposal.ts ├── jest.config.js ├── .gitignore ├── typedoc.json ├── .github └── workflows │ ├── npm-publish.yml │ ├── main.yml │ └── typedoc.yml ├── tsconfig.json ├── .eslintrc.js ├── LICENSE ├── webpack.config.js └── package.json /src/core/gov/proposals/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TextProposal'; 2 | -------------------------------------------------------------------------------- /src/util/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hash'; 2 | export * from './contract'; 3 | -------------------------------------------------------------------------------- /src/core/params/proposals/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ParameterChangeProposal'; 2 | -------------------------------------------------------------------------------- /src/core/distribution/proposals/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CommunityPoolSpendProposal'; 2 | -------------------------------------------------------------------------------- /img/terrajs-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/terra.js/master/img/terrajs-diagram.png -------------------------------------------------------------------------------- /src/key/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Key'; 2 | export * from './MnemonicKey'; 3 | export * from './RawKey'; 4 | -------------------------------------------------------------------------------- /integration-tests/contract.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/terra.js/master/integration-tests/contract.wasm -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | preset: 'ts-jest', 4 | roots: ['/src'], 5 | }; 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './core'; 2 | export * from './key'; 3 | export * from './client'; 4 | export * from './extension'; 5 | export * from './util'; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | dist 5 | coverage/ 6 | .idea/ 7 | .vscode/ 8 | docs 9 | .pytest_cache 10 | yarn.lock 11 | dist 12 | -------------------------------------------------------------------------------- /src/client/lcd/api/BaseAPI.ts: -------------------------------------------------------------------------------- 1 | import { APIRequester } from '../APIRequester'; 2 | 3 | export abstract class BaseAPI { 4 | constructor(protected c: APIRequester) {} 5 | } 6 | -------------------------------------------------------------------------------- /src/core/oracle/AggregateExchangeRatePrevote.data.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "a1bb7bf2f44620dad47c9779d8a90c094fafd4ba", 3 | "voter": "terravaloper1krj7amhhagjnyg2tkkuh6l0550y733jnjnnlzy", 4 | "submit_block": "4302940" 5 | } -------------------------------------------------------------------------------- /src/core/slashing/msgs/index.ts: -------------------------------------------------------------------------------- 1 | import { MsgUnjail } from './MsgUnjail'; 2 | 3 | export * from './MsgUnjail'; 4 | 5 | export type SlashingMsg = MsgUnjail; 6 | export namespace SlashingMsg { 7 | export type Data = MsgUnjail.Data; 8 | } 9 | -------------------------------------------------------------------------------- /integration-tests/validatorsWithVotingPower.ts: -------------------------------------------------------------------------------- 1 | import { LCDClient } from '../src'; 2 | 3 | const terra = new LCDClient({ 4 | chainID: 'bombay-9', 5 | URL: 'https://bombay-lcd.terra.dev', 6 | }); 7 | 8 | terra.utils.validatorsWithVotingPower().then(x => console.log(x)); 9 | -------------------------------------------------------------------------------- /src/client/index.ts: -------------------------------------------------------------------------------- 1 | // LCDClient 2 | export * from './lcd/LCDClient'; 3 | export * from './lcd/Wallet'; 4 | export * from './lcd/api'; 5 | 6 | // LocalTerra (LCDClient) 7 | export * from './LocalTerra'; 8 | 9 | // WebSocketClient 10 | export * from './WebSocketClient'; 11 | -------------------------------------------------------------------------------- /src/client/lcd/LCDClient.spec.ts: -------------------------------------------------------------------------------- 1 | // import { LCDClient } from './LCDClient'; 2 | 3 | describe('LCDClient', () => { 4 | it('runs', async () => { 5 | // const terra = new LCDClient({ 6 | // chainID: 'columbus-4', 7 | // URL: 'https://lcd.terra.dev', 8 | // }); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/core/StdTx.spec.ts: -------------------------------------------------------------------------------- 1 | import { StdTx } from './StdTx'; 2 | const StdTxData = require('./StdTx.data.json'); 3 | 4 | describe('StdTx', () => { 5 | it('deserializes', () => { 6 | StdTxData.forEach((tx: any) => { 7 | expect(tx).toMatchObject(StdTx.fromData(tx).toData()); 8 | }); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src"], 3 | "out": "docs", 4 | "excludeExternals": true, 5 | "exclude": "**/*+(index|.spec|.e2e).ts", 6 | "theme": "default", 7 | "name": "Terra.js", 8 | "excludePrivate": true, 9 | "excludeProtected": true, 10 | "media": "img", 11 | "hideGenerator": true 12 | } -------------------------------------------------------------------------------- /src/core/msgauth/msgs/MsgRevokeAuthorization.data.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "type": "msgauth/MsgRevokeAuthorization", 3 | "value": { 4 | "grantee": "terra1ruz7ugyh8sazpswsvt2fj54xcp68zu7wwsrsf4", 5 | "granter": "terra1na2r5d5ele6hh2fz44avgzw5cxvem2j0aaz0nk", 6 | "msg_type_url": "/cosmos.bank.v1beta1.MsgSend" 7 | } 8 | }] -------------------------------------------------------------------------------- /src/core/bank/msgs/index.ts: -------------------------------------------------------------------------------- 1 | import { MsgSend } from './MsgSend'; 2 | import { MsgMultiSend } from './MsgMultiSend'; 3 | 4 | export * from './MsgSend'; 5 | export * from './MsgMultiSend'; 6 | 7 | export type BankMsg = MsgSend | MsgMultiSend; 8 | export namespace BankMsg { 9 | export type Data = MsgSend.Data | MsgMultiSend.Data; 10 | } 11 | -------------------------------------------------------------------------------- /src/core/market/msgs/index.ts: -------------------------------------------------------------------------------- 1 | import { MsgSwap } from './MsgSwap'; 2 | export * from './MsgSwap'; 3 | 4 | import { MsgSwapSend } from './MsgSwapSend'; 5 | export * from './MsgSwapSend'; 6 | 7 | export type MarketMsg = MsgSwap | MsgSwapSend; 8 | 9 | export namespace MarketMsg { 10 | export type Data = MsgSwap.Data | MsgSwapSend.Data; 11 | } 12 | -------------------------------------------------------------------------------- /src/core/ValidatorSet.ts: -------------------------------------------------------------------------------- 1 | import { ValConsAddress, ValConsPubKey } from './bech32'; 2 | 3 | export interface ValidatorSet { 4 | block_height: string; 5 | validators: DelegateValidator[]; 6 | } 7 | 8 | export interface DelegateValidator { 9 | address: ValConsAddress; 10 | pub_key: ValConsPubKey.Data; 11 | proposer_priority: string; 12 | voting_power: string; 13 | } 14 | -------------------------------------------------------------------------------- /integration-tests/mnemonickey.ts: -------------------------------------------------------------------------------- 1 | import { MnemonicKey } from '../src'; 2 | 3 | const mk = new MnemonicKey({ 4 | mnemonic: 5 | 'satisfy adjust timber high purchase tuition stool faith fine install that you unaware feed domain license impose boss human eager hat rent enjoy dawn', 6 | coinType: 118, 7 | account: 5, 8 | index: 32, 9 | }); 10 | 11 | console.log(mk.accAddress); 12 | -------------------------------------------------------------------------------- /integration-tests/fromContractEvents.ts: -------------------------------------------------------------------------------- 1 | import { LCDClient, getContractEvents } from '../src'; 2 | 3 | const tequila = new LCDClient({ 4 | chainID: 'tequila-0004', 5 | URL: 'https://tequila-lcd.terra.dev', 6 | }); 7 | 8 | tequila.tx 9 | .txInfo('B652DF530D50E470070F3F211519495078082D01B49ED36B762B4E9446CE484E') 10 | .then(txInfo => getContractEvents(txInfo)) 11 | .then(console.log); 12 | -------------------------------------------------------------------------------- /src/core/oracle/AggregateExchangeRateVote.spec.ts: -------------------------------------------------------------------------------- 1 | import { AggregateExchangeRateVote } from './AggregateExchangeRateVote'; 2 | const data = require('./AggregateExchangeRateVote.data.json'); 3 | 4 | describe('AggregateExchangeRateVote', () => { 5 | it('deserializes', () => { 6 | const obj = AggregateExchangeRateVote.fromData(data); 7 | expect(obj.toData()).toMatchObject(data); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/core/msgauth/msgs/MsgExecAuthorized.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgExecAuthorized } from './MsgExecAuthorized'; 2 | const examples = require('./MsgExecAuthorized.data.json'); 3 | 4 | describe('MsgExecAuthorized', () => { 5 | it('deserializes', () => { 6 | examples.forEach((data: MsgExecAuthorized.Data) => { 7 | expect(MsgExecAuthorized.fromData(data).toData()).toEqual(data); 8 | }); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/core/auth/index.ts: -------------------------------------------------------------------------------- 1 | import { Account } from './Account'; 2 | import { LazyGradedVestingAccount } from './LazyGradedVestingAccount'; 3 | 4 | export * from './Account'; 5 | export * from './LazyGradedVestingAccount'; 6 | 7 | export function isVesting( 8 | account: Account | LazyGradedVestingAccount 9 | ): account is LazyGradedVestingAccount { 10 | return account instanceof LazyGradedVestingAccount; 11 | } 12 | -------------------------------------------------------------------------------- /src/core/staking/Delegation.spec.ts: -------------------------------------------------------------------------------- 1 | const delgsData = require('./Delegation.data.json'); 2 | import { Delegation } from './Delegation'; 3 | 4 | describe('Delegation', () => { 5 | it('deserializes', () => { 6 | delgsData.forEach((delgExample: Delegation.Data) => { 7 | const delg = Delegation.fromData(delgExample); 8 | expect(delg.toData()).toMatchObject(delgExample); 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /src/core/oracle/AggregateExchangeRatePrevote.spec.ts: -------------------------------------------------------------------------------- 1 | import { AggregateExchangeRatePrevote } from './AggregateExchangeRatePrevote'; 2 | const data = require('./AggregateExchangeRatePrevote.data.json'); 3 | 4 | describe('AggregateExchangeRatePrevote', () => { 5 | it('deserializes', () => { 6 | const obj = AggregateExchangeRatePrevote.fromData(data); 7 | expect(obj.toData()).toMatchObject(data); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/core/msgauth/msgs/MsgGrantAuthorization.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgGrantAuthorization } from './MsgGrantAuthorization'; 2 | const examples = require('./MsgGrantAuthorization.data.json'); 3 | 4 | describe('MsgGrantAuthorization', () => { 5 | it('deserializes', () => { 6 | examples.forEach((data: MsgGrantAuthorization.Data) => { 7 | expect(MsgGrantAuthorization.fromData(data).toData()).toEqual(data); 8 | }); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/core/msgauth/msgs/MsgRevokeAuthorization.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgRevokeAuthorization } from './MsgRevokeAuthorization'; 2 | const examples = require('./MsgRevokeAuthorization.data.json'); 3 | 4 | describe('MsgRevokeAuthorization', () => { 5 | it('deserializes', () => { 6 | examples.forEach((data: MsgRevokeAuthorization.Data) => { 7 | expect(MsgRevokeAuthorization.fromData(data).toData()).toEqual(data); 8 | }); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: NPM publish 2 | on: 3 | release: 4 | types: [published] 5 | 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v1 11 | - uses: actions/setup-node@v1 12 | with: 13 | node-version: 14 14 | - run: npm install 15 | - run: npm test 16 | - uses: JS-DevTools/npm-publish@v1 17 | with: 18 | token: ${{ secrets.NPM_TOKEN }} 19 | -------------------------------------------------------------------------------- /src/core/staking/Redelegation.spec.ts: -------------------------------------------------------------------------------- 1 | const data = require('./Redelegation.data.json'); 2 | import { Redelegation } from './Redelegation'; 3 | 4 | describe('Redelegation', () => { 5 | it('deserializes', () => { 6 | data.forEach((redelgExample: Redelegation.Data) => { 7 | Redelegation.fromData(redelgExample); 8 | // expect(redelg.toData()).toMatchObject(redelgExample); 9 | // JavaScript's Date does not preserve ns precision 10 | }); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/core/market/msgs/MsgSwap.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgSwap } from './MsgSwap'; 2 | const MsgSwapData = require('./MsgSwap.data.json'); 3 | 4 | describe('MsgSwap', () => { 5 | it('deserializes', () => { 6 | MsgSwapData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'market/MsgSwap') { 9 | const e = MsgSwap.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/util/convert.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from './json'; 2 | import { Dec, Numeric } from '../core/numeric'; 3 | 4 | export namespace Convert { 5 | export const id = (c: any): any => c; 6 | export const toDec = (c: Numeric.Input): Dec => new Dec(c); 7 | export const toString = (c: any): string => c.toString(); 8 | export const toFixed = (c: number): string => c.toFixed(); 9 | export const toNumber = Number.parseInt; 10 | export const toData = (c: JSONSerializable): any => c.toData(); 11 | } 12 | -------------------------------------------------------------------------------- /src/core/gov/msgs/index.ts: -------------------------------------------------------------------------------- 1 | import { MsgSubmitProposal } from './MsgSubmitProposal'; 2 | import { MsgVote } from './MsgVote'; 3 | import { MsgDeposit } from './MsgDeposit'; 4 | 5 | export * from './MsgDeposit'; 6 | export * from './MsgSubmitProposal'; 7 | export * from './MsgVote'; 8 | export * from './MsgVoteWeighted'; 9 | 10 | export type GovMsg = MsgDeposit | MsgSubmitProposal | MsgVote; 11 | 12 | export namespace GovMsg { 13 | export type Data = MsgDeposit.Data | MsgSubmitProposal.Data | MsgVote.Data; 14 | } 15 | -------------------------------------------------------------------------------- /src/client/lcd/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AuthAPI'; 2 | export * from './BankAPI'; 3 | export * from './DistributionAPI'; 4 | export * from './GovAPI'; 5 | export * from './MarketAPI'; 6 | export * from './MsgAuthAPI'; 7 | export * from './OracleAPI'; 8 | export * from './SlashingAPI'; 9 | export * from './StakingAPI'; 10 | export * from './SupplyAPI'; 11 | export * from './TendermintAPI'; 12 | export * from './TreasuryAPI'; 13 | export * from './TxAPI'; 14 | export * from './WasmAPI'; 15 | export * from './MintAPI'; 16 | -------------------------------------------------------------------------------- /src/core/staking/UnbondingDelegation.spec.ts: -------------------------------------------------------------------------------- 1 | const data = require('./UnbondingDelegation.data.json'); 2 | import { UnbondingDelegation } from './UnbondingDelegation'; 3 | 4 | describe('UnbondingDelegation', () => { 5 | it('deserializes', () => { 6 | data.forEach((udelgExample: UnbondingDelegation.Data) => { 7 | UnbondingDelegation.fromData(udelgExample); 8 | // expect(udelg.toData()).toMatchObject(udelgExample); 9 | // JavaScript's Date does not preserve ns precision 10 | }); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/core/gov/msgs/MsgDeposit.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgDeposit } from './MsgDeposit'; 2 | const MsgDepositData = require('./MsgDeposit.data.json'); 3 | 4 | describe('MsgDeposit', () => { 5 | it('deserializes', () => { 6 | MsgDepositData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'gov/MsgDeposit') { 9 | const e = MsgDeposit.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/core/slashing/msgs/MsgUnjail.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgUnjail } from './MsgUnjail'; 2 | const MsgUnjailData = require('./MsgUnjail.data.json'); 3 | 4 | describe('MsgUnjail', () => { 5 | it('deserializes', () => { 6 | MsgUnjailData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'cosmos/MsgUnjail') { 9 | const e = MsgUnjail.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/core/staking/msgs/MsgDelegate.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgDelegate } from './MsgDelegate'; 2 | const MsgDelegateData = require('./MsgDelegate.data.json'); 3 | 4 | describe('MsgDelegate', () => { 5 | it('deserializes', () => { 6 | MsgDelegateData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'staking/MsgDelegate') { 9 | const e = MsgDelegate.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/client/lcd/api/WasmAPI.spec.ts: -------------------------------------------------------------------------------- 1 | import { APIRequester } from '../APIRequester'; 2 | import { WasmAPI } from './WasmAPI'; 3 | 4 | const c = new APIRequester('https://bombay-lcd.terra.dev/'); 5 | const wasm = new WasmAPI(c); 6 | 7 | describe('WasmAPI', () => { 8 | it('parameters', async () => { 9 | await expect(wasm.parameters()).resolves.toMatchObject({ 10 | max_contract_size: expect.any(Number), 11 | max_contract_gas: expect.any(Number), 12 | max_contract_msg_size: expect.any(Number), 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/core/staking/msgs/MsgUndelegate.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgUndelegate } from './MsgUndelegate'; 2 | const MsgUndelegateData = require('./MsgUndelegate.data.json'); 3 | 4 | describe('MsgUndelegate', () => { 5 | it('deserializes', () => { 6 | MsgUndelegateData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'staking/MsgUndelegate') { 9 | const e = MsgUndelegate.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/core/staking/msgs/MsgEditValidator.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgEditValidator } from './MsgEditValidator'; 2 | const MsgEditValidatorData = require('./MsgEditValidator.data.json'); 3 | 4 | describe('MsgEditValidator', () => { 5 | it('deserializes', () => { 6 | MsgEditValidatorData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'staking/MsgEditValidator') { 9 | const e = MsgEditValidator.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/client/lcd/api/MarketAPI.spec.ts: -------------------------------------------------------------------------------- 1 | import { APIRequester } from '../APIRequester'; 2 | import { MarketAPI } from './MarketAPI'; 3 | import { Dec } from '../../../core/numeric'; 4 | 5 | const c = new APIRequester('https://bombay-lcd.terra.dev/'); 6 | const market = new MarketAPI(c); 7 | 8 | describe('MarketAPI', () => { 9 | it('parameters', async () => { 10 | await expect(market.parameters()).resolves.toMatchObject({ 11 | pool_recovery_period: expect.any(Number), 12 | base_pool: expect.any(Dec), 13 | min_stability_spread: expect.any(Dec), 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/core/staking/msgs/MsgBeginRedelegate.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgBeginRedelegate } from './MsgBeginRedelegate'; 2 | const MsgBeginRedelegateData = require('./MsgBeginRedelegate.data.json'); 3 | 4 | describe('MsgBeginRedelegate', () => { 5 | it('deserializes', () => { 6 | MsgBeginRedelegateData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'staking/MsgBeginRedelegate') { 9 | const e = MsgBeginRedelegate.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/core/staking/msgs/MsgCreateValidator.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgCreateValidator } from './MsgCreateValidator'; 2 | const MsgCreateValidatorData = require('./MsgCreateValidator.data.json'); 3 | 4 | describe('MsgCreateValidator', () => { 5 | it('deserializes', () => { 6 | MsgCreateValidatorData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'staking/MsgCreateValidator') { 9 | const e = MsgCreateValidator.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/core/oracle/msgs/MsgDelegateFeedConsent.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgDelegateFeedConsent } from './MsgDelegateFeedConsent'; 2 | const MsgDelegateFeedConsentData = require('./MsgDelegateFeedConsent.data.json'); 3 | 4 | describe('MsgDelegateFeedConsent', () => { 5 | it('deserializes', () => { 6 | MsgDelegateFeedConsentData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'oracle/MsgDelegateFeedConsent') { 9 | const e = MsgDelegateFeedConsent.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/core/distribution/msgs/MsgModifyWithdrawAddress.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgModifyWithdrawAddress } from './MsgModifyWithdrawAddress'; 2 | const MsgModifyWithdrawAddressData = require('./MsgModifyWithdrawAddress.data.json'); 3 | 4 | describe('MsgModifyWithdrawAddress', () => { 5 | it('deserializes', () => { 6 | MsgModifyWithdrawAddressData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'distribution/MsgModifyWithdrawAddress') { 9 | const e = MsgModifyWithdrawAddress.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/core/msgauth/msgs/index.ts: -------------------------------------------------------------------------------- 1 | import { MsgGrantAuthorization } from './MsgGrantAuthorization'; 2 | import { MsgRevokeAuthorization } from './MsgRevokeAuthorization'; 3 | import { MsgExecAuthorized } from './MsgExecAuthorized'; 4 | 5 | export * from './MsgGrantAuthorization'; 6 | export * from './MsgRevokeAuthorization'; 7 | export * from './MsgExecAuthorized'; 8 | 9 | export type MsgAuthMsg = 10 | | MsgGrantAuthorization 11 | | MsgRevokeAuthorization 12 | | MsgExecAuthorized; 13 | 14 | export namespace MsgAuthMsg { 15 | export type Data = 16 | | MsgGrantAuthorization.Data 17 | | MsgRevokeAuthorization.Data 18 | | MsgExecAuthorized.Data; 19 | } 20 | -------------------------------------------------------------------------------- /src/util/hash.ts: -------------------------------------------------------------------------------- 1 | import { SHA256 } from 'jscrypto/SHA256'; 2 | import { Base64 } from 'jscrypto/Base64'; 3 | /* 4 | DEPRECATED (was used by crypto-js) 5 | function byteArrayToWordArray(ba: Uint8Array): CryptoJS.LibWordArray { 6 | const wa: number[] = []; 7 | for (let i = 0; i < ba.length; i += 1) { 8 | wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i); 9 | } 10 | return crypto.lib.WordArray.create(wa); 11 | }*/ 12 | 13 | /** 14 | * Calculates the transaction hash from Amino-encoded string. 15 | * @param txData Amino-encoded string (base64) 16 | */ 17 | export function hashAmino(txData: string): string { 18 | return SHA256.hash(Base64.parse(txData)).toString().toUpperCase(); 19 | } 20 | -------------------------------------------------------------------------------- /src/client/lcd/api/DistributionAPI.spec.ts: -------------------------------------------------------------------------------- 1 | import { APIRequester } from '../APIRequester'; 2 | import { DistributionAPI } from './DistributionAPI'; 3 | 4 | import { Dec } from '../../../core'; 5 | 6 | const c = new APIRequester('https://bombay-lcd.terra.dev/'); 7 | const distribution = new DistributionAPI(c); 8 | 9 | describe('DistributionAPI', () => { 10 | it('parameters', async () => { 11 | await expect(distribution.parameters()).resolves.toMatchObject({ 12 | community_tax: expect.any(Dec), 13 | base_proposer_reward: expect.any(Dec), 14 | bonus_proposer_reward: expect.any(Dec), 15 | withdraw_addr_enabled: expect.any(Boolean), 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/core/distribution/msgs/MsgWithdrawDelegationReward.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgWithdrawDelegationReward } from './MsgWithdrawDelegationReward'; 2 | const MsgWithdrawDelegationRewardData = require('./MsgWithdrawDelegationReward.data.json'); 3 | 4 | describe('MsgWithdrawDelegationReward', () => { 5 | it('deserializes', () => { 6 | MsgWithdrawDelegationRewardData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'distribution/MsgWithdrawDelegationReward') { 9 | const e = MsgWithdrawDelegationReward.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/core/gov/msgs/MsgSubmitProposal.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgSubmitProposal } from './MsgSubmitProposal'; 2 | const MsgSubmitProposalData = require('./MsgSubmitProposal.data.json'); 3 | 4 | describe('MsgSubmitProposal', () => { 5 | it('deserializes', () => { 6 | MsgSubmitProposalData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'gov/MsgSubmitProposal') { 9 | // we are not checking for equality of toData() because toJSON() doesn't marshal 10 | // ParamaterChangeProposal's JSON keys in the same order 11 | MsgSubmitProposal.fromData(msg); 12 | } 13 | }); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/core/oracle/msgs/MsgAggregateExchangeRateVote.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgAggregateExchangeRateVote } from './MsgAggregateExchangeRateVote'; 2 | 3 | describe('MsgAggregateExchangeRateVote', () => { 4 | it('getAggregateVoteHash', () => { 5 | const msg = new MsgAggregateExchangeRateVote( 6 | { 7 | ukrw: '245.000', 8 | uusd: '0.2242', 9 | usdr: '0.182', 10 | }, 11 | 'salt', 12 | 'terra1krj7amhhagjnyg2tkkuh6l0550y733jnjulzjh', 13 | 'terravaloper1krj7amhhagjnyg2tkkuh6l0550y733jnjnnlzy' 14 | ); 15 | msg.getPrevote(); 16 | expect(msg.getAggregateVoteHash()).toEqual( 17 | '7929908433e7399845fa60f9ef70ef7f2bb8f01b' 18 | ); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /integration-tests/clikey.ts: -------------------------------------------------------------------------------- 1 | import { StdFee, MsgSend } from '../src'; 2 | import { LocalTerra } from '../src'; 3 | import { CLIKey } from '../src/key/CLIKey'; 4 | 5 | const terra = new LocalTerra(); 6 | const { test1 } = terra.wallets; 7 | const cliKey = new CLIKey({ keyName: 'paul4' }); 8 | const cliWallet = terra.wallet(cliKey); 9 | 10 | const send = new MsgSend(cliWallet.key.accAddress, test1.key.accAddress, { 11 | uluna: 100000, 12 | }); 13 | 14 | async function main() { 15 | const tx = await cliWallet.createAndSignTx({ 16 | msgs: [send], 17 | fee: new StdFee(100000, { uluna: 100000 }), 18 | }); 19 | 20 | console.log(await terra.tx.broadcast(tx)); 21 | } 22 | 23 | main().catch(console.error); 24 | -------------------------------------------------------------------------------- /src/core/distribution/msgs/MsgWithdrawValidatorCommission.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgWithdrawValidatorCommission } from './MsgWithdrawValidatorCommission'; 2 | const MsgWithdrawValidatorCommissionData = require('./MsgWithdrawValidatorCommission.data.json'); 3 | 4 | describe('MsgWithdrawValidatorCommission', () => { 5 | it('deserializes', () => { 6 | MsgWithdrawValidatorCommissionData.txs.forEach((txinfo: any) => { 7 | txinfo.tx.value.msg.forEach((msg: any) => { 8 | if (msg.type == 'distribution/MsgWithdrawValidatorCommission') { 9 | const e = MsgWithdrawValidatorCommission.fromData(msg); 10 | expect(e.toData()).toEqual(msg); 11 | } 12 | }); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/util/json.spec.ts: -------------------------------------------------------------------------------- 1 | import { removeNull } from './json'; 2 | 3 | describe('removeNull', () => { 4 | it('remove null ', () => { 5 | expect( 6 | removeNull({ 7 | a: 'abc', 8 | b: { 9 | a: null, 10 | b: 123, 11 | }, 12 | c: null, 13 | d: [123], 14 | e: { 15 | a: { 16 | a: null, 17 | b: 'abc', 18 | }, 19 | b: 123, 20 | }, 21 | }) 22 | ).toEqual({ 23 | a: 'abc', 24 | b: { 25 | b: 123, 26 | }, 27 | d: [123], 28 | e: { 29 | a: { 30 | b: 'abc', 31 | }, 32 | b: 123, 33 | }, 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/client/lcd/api/SlashingAPI.spec.ts: -------------------------------------------------------------------------------- 1 | import { APIRequester } from '../APIRequester'; 2 | import { SlashingAPI } from './SlashingAPI'; 3 | import { Dec } from '../../../core/numeric'; 4 | 5 | const c = new APIRequester('https://bombay-lcd.terra.dev/'); 6 | const slashing = new SlashingAPI(c); 7 | 8 | describe('SlashingAPI', () => { 9 | it('parameters', async () => { 10 | await expect(slashing.parameters()).resolves.toMatchObject({ 11 | signed_blocks_window: expect.any(Number), 12 | min_signed_per_window: expect.any(Dec), 13 | downtime_jail_duration: expect.any(Number), 14 | slash_fraction_double_sign: expect.any(Dec), 15 | slash_fraction_downtime: expect.any(Dec), 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/core/oracle/msgs/index.ts: -------------------------------------------------------------------------------- 1 | import { MsgDelegateFeedConsent } from './MsgDelegateFeedConsent'; 2 | import { MsgAggregateExchangeRatePrevote } from './MsgAggregateExchangeRatePrevote'; 3 | import { MsgAggregateExchangeRateVote } from './MsgAggregateExchangeRateVote'; 4 | 5 | export * from './MsgDelegateFeedConsent'; 6 | export * from './MsgAggregateExchangeRateVote'; 7 | export * from './MsgAggregateExchangeRatePrevote'; 8 | 9 | export type OracleMsg = 10 | | MsgDelegateFeedConsent 11 | | MsgAggregateExchangeRateVote 12 | | MsgAggregateExchangeRatePrevote; 13 | 14 | export namespace OracleMsg { 15 | export type Data = 16 | | MsgDelegateFeedConsent.Data 17 | | MsgAggregateExchangeRateVote.Data 18 | | MsgAggregateExchangeRatePrevote.Data; 19 | } 20 | -------------------------------------------------------------------------------- /src/client/lcd/api/BankAPI.spec.ts: -------------------------------------------------------------------------------- 1 | import { APIRequester } from '../APIRequester'; 2 | import { BankAPI } from './BankAPI'; 3 | import { Coins } from '../../../core'; 4 | 5 | const c = new APIRequester('https://bombay-lcd.terra.dev/'); 6 | const bank = new BankAPI(c); 7 | 8 | describe('BankAPI', () => { 9 | describe('balance', () => { 10 | it('account exists', async () => { 11 | await bank.balance('terra1ax7xtll5v6u6vdnymxa4k4648w80zhkggl0u24'); 12 | }); 13 | 14 | it('invalid account', async () => { 15 | await expect(bank.balance('1234')).rejects.toThrow(); 16 | }); 17 | }); 18 | 19 | it('total supply', async () => { 20 | const totalSupply = await bank.total(); 21 | expect(totalSupply).toEqual(expect.any(Coins)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/core/Denom.ts: -------------------------------------------------------------------------------- 1 | export type Denom = string; 2 | 3 | export namespace Denom { 4 | export const LUNA = 'uluna'; 5 | export const AUD = 'uaud'; 6 | export const CAD = 'ucad'; 7 | export const CHF = 'uchf'; 8 | export const CNY = 'ucny'; 9 | export const DKK = 'udkk'; 10 | export const EUR = 'ueur'; 11 | export const GBP = 'ugbp'; 12 | export const HKD = 'uhkd'; 13 | export const IDR = 'uidr'; 14 | export const INR = 'uinr'; 15 | export const JPY = 'ujpy'; 16 | export const KRW = 'ukrw'; 17 | export const MNT = 'umnt'; 18 | export const NOK = 'unok'; 19 | export const PHP = 'uphp'; 20 | export const SDR = 'usdr'; 21 | export const SEK = 'usek'; 22 | export const SGD = 'usgd'; 23 | export const THB = 'uthb'; 24 | export const USD = 'uusd'; 25 | } 26 | -------------------------------------------------------------------------------- /src/client/lcd/api/GovAPI.spec.ts: -------------------------------------------------------------------------------- 1 | import { APIRequester } from '../APIRequester'; 2 | import { GovAPI } from './GovAPI'; 3 | import { Coins, Dec } from '../../../core'; 4 | 5 | const c = new APIRequester('https://bombay-lcd.terra.dev/'); 6 | const gov = new GovAPI(c); 7 | 8 | describe('GovAPI', () => { 9 | it('parameters', async () => { 10 | await expect(gov.parameters()).resolves.toMatchObject({ 11 | deposit_params: { 12 | min_deposit: expect.any(Coins), 13 | max_deposit_period: expect.any(Number), 14 | }, 15 | voting_params: { 16 | voting_period: expect.any(Number), 17 | }, 18 | tally_params: { 19 | quorum: expect.any(Dec), 20 | threshold: expect.any(Dec), 21 | veto_threshold: expect.any(Dec), 22 | }, 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/client/lcd/api/TreasuryAPI.spec.ts: -------------------------------------------------------------------------------- 1 | import { APIRequester } from '../APIRequester'; 2 | import { TreasuryAPI } from './TreasuryAPI'; 3 | import { Dec, PolicyConstraints } from '../../../core'; 4 | 5 | const c = new APIRequester('https://bombay-lcd.terra.dev/'); 6 | const treasury = new TreasuryAPI(c); 7 | 8 | describe('TreasuryAPI', () => { 9 | it('parameters', async () => { 10 | await expect(treasury.parameters()).resolves.toMatchObject({ 11 | tax_policy: expect.any(PolicyConstraints), 12 | reward_policy: expect.any(PolicyConstraints), 13 | seigniorage_burden_target: expect.any(Dec), 14 | mining_increment: expect.any(Dec), 15 | window_short: expect.any(Number), 16 | window_long: expect.any(Number), 17 | window_probation: expect.any(Number), 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/client/lcd/api/SupplyAPI.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Total supply API has been moved bank module 3 | * This file will be deprecated. DO NOT USE 4 | */ 5 | import { Coins } from '../../../core'; 6 | import { BaseAPI } from './BaseAPI'; 7 | import { APIParams } from '../APIRequester'; 8 | 9 | export interface SupplyResponse { 10 | supply: Coins; 11 | } 12 | 13 | export namespace SupplyResponse { 14 | export interface Data { 15 | supply: Coins.Data; 16 | } 17 | } 18 | 19 | export class SupplyAPI extends BaseAPI { 20 | /** 21 | * Get the total supply of tokens in circulation for all denominations. 22 | */ 23 | public async total(params: APIParams = {}): Promise { 24 | return this.c 25 | .get(`/bank/total`, params) 26 | .then(d => Coins.fromData(d.result.supply)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/core/msgauth/msgs/MsgExecAuthorized.data.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "type": "msgauth/MsgExecAuthorized", 3 | "value": { 4 | "grantee": "terra...", 5 | "msgs": [{ 6 | "type": "bank/MsgSend", 7 | "value": { 8 | "from_address": "terra...", 9 | "to_address": "terra...", 10 | "amount": [{ 11 | "denom": "ukrw", 12 | "amount": "999" 13 | }] 14 | } 15 | }, { 16 | "type": "market/MsgSwap", 17 | "value": { 18 | "trader": "terra...", 19 | "offer_coin": { 20 | "denom": "uusd", 21 | "amount": "120391203" 22 | }, 23 | "ask_denom": "ukrw" 24 | } 25 | }] 26 | } 27 | }] -------------------------------------------------------------------------------- /src/client/lcd/LCDUtils.spec.ts: -------------------------------------------------------------------------------- 1 | import { LCDClient } from './LCDClient'; 2 | import { LCDUtils } from './LCDUtils'; 3 | import { Coin, Validator } from '../../core'; 4 | 5 | const lcdUtils = new LCDUtils( 6 | new LCDClient({ 7 | chainID: 'bombay-9', 8 | URL: 'https://bombay-lcd.terra.dev', 9 | }) 10 | ); 11 | 12 | describe('LCDUtils', () => { 13 | it('calculateTax', async () => { 14 | await expect( 15 | lcdUtils.calculateTax(new Coin('uluna', '0.0')) 16 | ).resolves.toBeInstanceOf(Coin); 17 | }); 18 | 19 | it('validatorsWithVotingPower', async () => { 20 | const vwv = await lcdUtils.validatorsWithVotingPower(); 21 | 22 | expect(vwv[Object.keys(vwv)[0]]).toMatchObject({ 23 | validatorInfo: expect.any(Validator), 24 | votingPower: expect.any(Number), 25 | proposerPriority: expect.any(Number), 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/client/lcd/api/BankAPI.ts: -------------------------------------------------------------------------------- 1 | import { BaseAPI } from './BaseAPI'; 2 | import { Coins, AccAddress } from '../../../core'; 3 | import { APIParams } from '../APIRequester'; 4 | 5 | export class BankAPI extends BaseAPI { 6 | /** 7 | * Look up the balance of an account by its address. 8 | * @param address address of account to look up. 9 | */ 10 | public async balance( 11 | address: AccAddress, 12 | params: APIParams = {} 13 | ): Promise { 14 | return this.c 15 | .get(`/bank/balances/${address}`, params) 16 | .then(d => Coins.fromData(d.result)); 17 | } 18 | 19 | /** 20 | * Get the total supply of tokens in circulation for all denominations. 21 | */ 22 | public async total(): Promise { 23 | return this.c 24 | .get<{ supply: Coins.Data }>(`/bank/total`) 25 | .then(d => Coins.fromData(d.result.supply)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/core/staking/msgs/index.ts: -------------------------------------------------------------------------------- 1 | import { MsgDelegate } from './MsgDelegate'; 2 | import { MsgUndelegate } from './MsgUndelegate'; 3 | import { MsgBeginRedelegate } from './MsgBeginRedelegate'; 4 | import { MsgCreateValidator } from './MsgCreateValidator'; 5 | import { MsgEditValidator } from './MsgEditValidator'; 6 | 7 | export * from './MsgDelegate'; 8 | export * from './MsgUndelegate'; 9 | export * from './MsgBeginRedelegate'; 10 | export * from './MsgCreateValidator'; 11 | export * from './MsgEditValidator'; 12 | 13 | export type StakingMsg = 14 | | MsgDelegate 15 | | MsgUndelegate 16 | | MsgBeginRedelegate 17 | | MsgCreateValidator 18 | | MsgEditValidator; 19 | 20 | export namespace StakingMsg { 21 | export type Data = 22 | | MsgDelegate.Data 23 | | MsgUndelegate.Data 24 | | MsgBeginRedelegate.Data 25 | | MsgCreateValidator.Data 26 | | MsgEditValidator.Data; 27 | } 28 | -------------------------------------------------------------------------------- /src/core/wasm/msgs/MsgExecuteContract.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgExecuteContract } from './MsgExecuteContract'; 2 | 3 | describe('MsgExecuteContract', () => { 4 | it('works when execute_msg is not JSON', () => { 5 | const msg1 = MsgExecuteContract.fromData({ 6 | type: 'wasm/MsgExecuteContract', 7 | value: { 8 | sender: 'terra16xw94u0jgmuaz8zs54xn9x96lxew74gs05gs4h', 9 | contract: 'terra15gwkyepfc6xgca5t5zefzwy42uts8l2m4g40k6', 10 | execute_msg: { 11 | transfer: { 12 | recipient: 'terra13jqgrtqwucx4jdvhg0d4tc80892fscx54298yt', 13 | amount: 10000, 14 | }, 15 | }, 16 | coins: [], 17 | }, 18 | }); 19 | 20 | expect(msg1.execute_msg).toMatchObject({ 21 | transfer: { 22 | recipient: 'terra13jqgrtqwucx4jdvhg0d4tc80892fscx54298yt', 23 | amount: 10000, 24 | }, 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/core/bank/msgs/MsgSend.spec.ts: -------------------------------------------------------------------------------- 1 | import { MsgSend } from './MsgSend'; 2 | import { Coins } from '../../Coins'; 3 | 4 | describe('MsgSend', () => { 5 | it('deserializes correctly', () => { 6 | const send = MsgSend.fromData({ 7 | type: 'bank/MsgSend', 8 | value: { 9 | from_address: 'terra1y4umfuqfg76t8mfcff6zzx7elvy93jtp4xcdvw', 10 | to_address: 'terra1v9ku44wycfnsucez6fp085f5fsksp47u9x8jr4', 11 | amount: [ 12 | { 13 | denom: 'uluna', 14 | amount: '8102024952', 15 | }, 16 | ], 17 | }, 18 | }); 19 | 20 | expect(send).toMatchObject({ 21 | from_address: 'terra1y4umfuqfg76t8mfcff6zzx7elvy93jtp4xcdvw', 22 | to_address: 'terra1v9ku44wycfnsucez6fp085f5fsksp47u9x8jr4', 23 | amount: new Coins({ 24 | uluna: 8102024952, 25 | }), 26 | }); 27 | 28 | expect(send.toData()).toMatchObject({}); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types"], 3 | "exclude": ["/node_modules/", "./src/**/*.spec.ts"], 4 | "compilerOptions": { 5 | "allowSyntheticDefaultImports": true, 6 | "alwaysStrict": true, 7 | "baseUrl": "./", 8 | "declaration": true, 9 | "esModuleInterop": true, 10 | "lib": ["es2015", "es2016", "es2017", "dom"], 11 | "module": "commonjs", 12 | "moduleResolution": "node", 13 | "noFallthroughCasesInSwitch": true, 14 | "noImplicitAny": true, 15 | "noImplicitReturns": true, 16 | "noImplicitThis": true, 17 | "noUnusedLocals": false, 18 | "noUnusedParameters": true, 19 | "outDir": "dist", 20 | "rootDir": "src", 21 | "sourceMap": true, 22 | "strict": true, 23 | "strictFunctionTypes": true, 24 | "strictNullChecks": true, 25 | "strictPropertyInitialization": true, 26 | "target": "es5", 27 | "paths": { 28 | "*": ["src/*"] 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/client/lcd/api/OracleAPI.spec.ts: -------------------------------------------------------------------------------- 1 | import { APIRequester } from '../APIRequester'; 2 | import { OracleAPI } from './OracleAPI'; 3 | import { Dec } from '../../../core/numeric'; 4 | 5 | const c = new APIRequester('https://bombay-lcd.terra.dev/'); 6 | const oracle = new OracleAPI(c); 7 | 8 | describe('OracleAPI', () => { 9 | it('parameters', async () => { 10 | await expect(oracle.parameters()).resolves.toMatchObject({ 11 | vote_period: expect.any(Number), 12 | vote_threshold: expect.any(Dec), 13 | reward_band: expect.any(Dec), 14 | reward_distribution_window: expect.any(Number), 15 | whitelist: expect.any(Array), 16 | slash_fraction: expect.any(Dec), 17 | slash_window: expect.any(Number), 18 | min_valid_per_window: expect.any(Dec), 19 | }); 20 | 21 | const params = await oracle.parameters(); 22 | expect(params.whitelist[0].tobin_tax).toEqual(expect.any(Dec)); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/client/lcd/api/MintAPI.spec.ts: -------------------------------------------------------------------------------- 1 | import { Dec } from '../../../core/numeric'; 2 | import { APIRequester } from '../APIRequester'; 3 | import { MintAPI } from './MintAPI'; 4 | 5 | const c = new APIRequester('https://bombay-lcd.terra.dev/'); 6 | const api = new MintAPI(c); 7 | 8 | describe('MintAPI', () => { 9 | it('inflation', async () => { 10 | await expect(api.inflation()).resolves.toBeInstanceOf(Dec); 11 | }); 12 | 13 | it('annual provisions', async () => { 14 | await expect(api.annualProvisions()).resolves.toBeInstanceOf(Dec); 15 | }); 16 | 17 | it('parameters', async () => { 18 | await expect(api.parameters()).resolves.toMatchObject({ 19 | mint_denom: expect.any(String), 20 | inflation_rate_change: expect.any(Dec), 21 | inflation_max: expect.any(Dec), 22 | inflation_min: expect.any(Dec), 23 | goal_bonded: expect.any(Dec), 24 | blocks_per_year: expect.any(Number), 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/core/distribution/msgs/index.ts: -------------------------------------------------------------------------------- 1 | import { MsgModifyWithdrawAddress } from './MsgModifyWithdrawAddress'; 2 | import { MsgWithdrawDelegationReward } from './MsgWithdrawDelegationReward'; 3 | import { MsgWithdrawValidatorCommission } from './MsgWithdrawValidatorCommission'; 4 | import { MsgFundCommunityPool } from './MsgFundCommunityPool'; 5 | 6 | export * from './MsgModifyWithdrawAddress'; 7 | export * from './MsgWithdrawDelegationReward'; 8 | export * from './MsgWithdrawValidatorCommission'; 9 | export * from './MsgFundCommunityPool'; 10 | 11 | export type DistributionMsg = 12 | | MsgModifyWithdrawAddress 13 | | MsgWithdrawDelegationReward 14 | | MsgWithdrawValidatorCommission 15 | | MsgFundCommunityPool; 16 | 17 | export namespace DistributionMsg { 18 | export type Data = 19 | | MsgModifyWithdrawAddress.Data 20 | | MsgWithdrawDelegationReward.Data 21 | | MsgWithdrawValidatorCommission.Data 22 | | MsgFundCommunityPool.Data; 23 | } 24 | -------------------------------------------------------------------------------- /integration-tests/websocket.ts: -------------------------------------------------------------------------------- 1 | import { LocalTerra, WebSocketClient } from '../src'; 2 | 3 | const wsclient = new WebSocketClient('ws://localhost:26657/websocket') 4 | const terra = new LocalTerra(); 5 | let count = 0; 6 | 7 | wsclient.subscribe('NewBlock', {}, (_, socket) => { 8 | console.log(count); 9 | count += 1; 10 | 11 | if (count === 3) { 12 | socket.close(); 13 | } 14 | }); 15 | 16 | // send tracker 17 | wsclient.subscribe( 18 | 'Tx', 19 | { 'message.action': 'send', 'message.e': ['CONTAINS', '3'] }, 20 | data => { 21 | console.log('Send occured!'); 22 | console.log(data.value); 23 | } 24 | ); 25 | 26 | // swap tracker 27 | wsclient.subscribeTx({ 'message.action': '/terra.market.v1beta1.MsgSwap' }, async data => { 28 | console.log('Swap occured!'); 29 | const txInfo = await terra.tx.txInfo(data.value.TxResult.txhash); 30 | if (txInfo.logs) { 31 | console.log(txInfo.logs[0].eventsByType); 32 | } 33 | }); 34 | 35 | wsclient.start() 36 | -------------------------------------------------------------------------------- /src/core/Int.spec.ts: -------------------------------------------------------------------------------- 1 | import { Int, int } from './numeric'; 2 | 3 | describe('Int', () => { 4 | it('tagged literal syntax', () => { 5 | const int1 = int`1234`; 6 | const int2 = new Int(1234); 7 | expect(int1).toEqual(int2); 8 | }); 9 | 10 | it('converts into integer value', () => { 11 | const i = new Int('100'); 12 | const i2 = new Int('100.293'); 13 | const i3 = new Int(100.123); 14 | expect(i).toEqual(i2); 15 | expect(i).toEqual(i3); 16 | expect(i3.toString()).toEqual('100'); 17 | }); 18 | 19 | it('accomodates large values', () => { 20 | const i = new Int('9999999999999999999'); 21 | const i2 = i.mul(10).add(5); 22 | expect(i2.toString()).toEqual(i.toString() + '5'); 23 | }); 24 | 25 | it('arithmetic', () => { 26 | const i = new Int('5'); 27 | const i2 = new Int('3'); 28 | expect(i.plus(i2).toNumber()).toEqual(8); 29 | expect(i.div(i2).toNumber()).toEqual(1); // truncates 1.66666 toward 1, not 2 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | jasmine: true, 7 | }, 8 | parser: '@typescript-eslint/parser', 9 | plugins: ['@typescript-eslint'], 10 | extends: [ 11 | 'eslint:recommended', 12 | 'plugin:@typescript-eslint/eslint-recommended', 13 | 'plugin:@typescript-eslint/recommended', 14 | ], 15 | rules: { 16 | '@typescript-eslint/camelcase': 'off', 17 | '@typescript-eslint/no-namespace': 'off', 18 | 'no-inner-declarations': 'off', 19 | // TODO: we have to figure out how to use eslint without below rules 20 | '@typescript-eslint/no-use-before-define': 'off', 21 | '@typescript-eslint/no-explicit-any': 'off', 22 | '@typescript-eslint/ban-ts-ignore': 'off', 23 | '@typescript-eslint/ban-ts-comment': 'off', 24 | '@typescript-eslint/no-var-requires': 'off', 25 | '@typescript-eslint/ban-types': 'off', 26 | '@typescript-eslint/explicit-module-boundary-types': 'off', 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | 7 | steps: 8 | - name: Begin CI... 9 | uses: actions/checkout@v2 10 | 11 | - name: Use Node 12 12 | uses: actions/setup-node@v1 13 | with: 14 | node-version: 12.x 15 | 16 | - name: Use cached node_modules 17 | uses: actions/cache@v1 18 | with: 19 | path: node_modules 20 | key: nodeModules-${{ hashFiles('**/package-lock.json') }} 21 | restore-keys: | 22 | nodeModules- 23 | 24 | - name: Install dependencies 25 | run: npm ci 26 | env: 27 | CI: true 28 | 29 | - name: Lint 30 | run: npm run lint 31 | env: 32 | CI: true 33 | 34 | - name: Test 35 | run: npm run test --ci --coverage --maxWorkers=2 36 | env: 37 | CI: true 38 | 39 | - name: Build 40 | run: npm run build 41 | env: 42 | CI: true 43 | -------------------------------------------------------------------------------- /.github/workflows/typedoc.yml: -------------------------------------------------------------------------------- 1 | name: TypeDoc 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | with: 15 | persist-credentials: false 16 | 17 | - name: Use cached node_modules 18 | uses: actions/cache@v1 19 | with: 20 | path: node_modules 21 | key: nodeModules-${{ hashFiles('**/package-lock.json') }} 22 | restore-keys: | 23 | nodeModules- 24 | 25 | - name: Install Dependencies 26 | run: npm ci 27 | 28 | - name: Build Docs 29 | run: npm run doc 30 | 31 | - name: Deploy 32 | uses: JamesIves/github-pages-deploy-action@releases/v3 33 | with: 34 | ACCESS_TOKEN: ${{ secrets.GH_TOKEN }} 35 | BRANCH: gh-pages 36 | FOLDER: docs 37 | GIT_CONFIG_NAME: Github Actions 38 | GIT_CONFIG_EMAIL: noreply@github.com 39 | -------------------------------------------------------------------------------- /src/util/json.ts: -------------------------------------------------------------------------------- 1 | export function prepareSignBytes(obj: any): any { 2 | if (Array.isArray(obj)) { 3 | return obj.map(prepareSignBytes); 4 | } 5 | 6 | // string or number 7 | if (typeof obj !== `object`) { 8 | return obj; 9 | } 10 | 11 | const sorted: any = {}; 12 | Object.keys(obj) 13 | .sort() 14 | .forEach(key => { 15 | if (obj[key] === undefined || obj[key] === null) return; 16 | sorted[key] = prepareSignBytes(obj[key]); 17 | }); 18 | return sorted; 19 | } 20 | 21 | export abstract class JSONSerializable { 22 | public abstract toData(): T; 23 | public toJSON(): string { 24 | return JSON.stringify(prepareSignBytes(this.toData())); 25 | } 26 | } 27 | 28 | export function removeNull(obj: any): any { 29 | if (obj !== null && typeof obj === 'object') { 30 | Object.keys(obj).forEach(function (key) { 31 | if (obj[key] === null) { 32 | delete obj[key]; 33 | } else if (typeof obj[key] === 'object') { 34 | removeNull(obj[key]); 35 | } 36 | }); 37 | } 38 | 39 | return obj; 40 | } 41 | -------------------------------------------------------------------------------- /src/key/RawKey.ts: -------------------------------------------------------------------------------- 1 | import { SHA256 } from 'jscrypto/SHA256'; 2 | import * as secp256k1 from 'secp256k1'; 3 | import { Key } from './Key'; 4 | 5 | /** 6 | * An implementation of the Key interfaces that uses a raw private key. 7 | */ 8 | export class RawKey extends Key { 9 | /** 10 | * Raw private key, in bytes. 11 | */ 12 | public privateKey: Buffer; 13 | 14 | constructor(privateKey: Buffer) { 15 | const publicKey = secp256k1.publicKeyCreate( 16 | new Uint8Array(privateKey), 17 | true 18 | ); 19 | super(Buffer.from(publicKey)); 20 | this.privateKey = privateKey; 21 | } 22 | 23 | public ecdsaSign(payload: Buffer): { signature: Uint8Array; recid: number } { 24 | const hash = Buffer.from(SHA256.hash(payload.toString()).toString(), 'hex'); 25 | return secp256k1.ecdsaSign( 26 | Uint8Array.from(hash), 27 | Uint8Array.from(this.privateKey) 28 | ); 29 | } 30 | 31 | public async sign(payload: Buffer): Promise { 32 | const { signature } = this.ecdsaSign(payload); 33 | return Buffer.from(signature); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/core/wasm/msgs/MsgStoreCode.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | 4 | export class MsgStoreCode extends JSONSerializable { 5 | /** 6 | * @param sender code creator 7 | * @param wasm_byte_code base64-encoded bytecode contents 8 | */ 9 | constructor(public sender: AccAddress, public wasm_byte_code: string) { 10 | super(); 11 | } 12 | 13 | public static fromData(data: MsgStoreCode.Data): MsgStoreCode { 14 | const { 15 | value: { sender, wasm_byte_code }, 16 | } = data; 17 | return new MsgStoreCode(sender, wasm_byte_code); 18 | } 19 | 20 | public toData(): MsgStoreCode.Data { 21 | const { sender, wasm_byte_code } = this; 22 | return { 23 | type: 'wasm/MsgStoreCode', 24 | value: { 25 | sender, 26 | wasm_byte_code, 27 | }, 28 | }; 29 | } 30 | } 31 | 32 | export namespace MsgStoreCode { 33 | export interface Data { 34 | type: 'wasm/MsgStoreCode'; 35 | value: { 36 | sender: AccAddress; 37 | wasm_byte_code: string; 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Terraform Labs, PTE. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /integration-tests/submitProposal.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Dec, 3 | LCDClient, 4 | MnemonicKey, 5 | MsgSubmitProposal, 6 | TextProposal, 7 | CommunityPoolSpendProposal, 8 | ParameterChangeProposal, 9 | } from '../src'; 10 | 11 | const client = new LCDClient({ 12 | chainID: 'bombay-9', 13 | URL: 'https://bombay-lcd.terra.dev', 14 | }); 15 | 16 | // LocalTerra test1 terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v 17 | const mk = new MnemonicKey({ 18 | mnemonic: 19 | 'notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius', 20 | }); 21 | 22 | const wallet = client.wallet(mk); 23 | 24 | async function main() { 25 | const execute = new MsgSubmitProposal( 26 | new TaxRateUpdateProposal('tax rate test', 'tax rate test', new Dec(0.2)), 27 | { uluna: 10000000 }, 28 | wallet.key.accAddress 29 | ); 30 | 31 | const executeTx = await wallet.createAndSignTx({ 32 | msgs: [execute], 33 | }); 34 | 35 | const executeTxResult = await client.tx.broadcastSync(executeTx); 36 | console.log(executeTxResult); 37 | } 38 | 39 | main().catch(console.error); 40 | -------------------------------------------------------------------------------- /src/client/lcd/Wallet.spec.ts: -------------------------------------------------------------------------------- 1 | // import { MsgSwap, Coin, StdFee } from '../../core'; 2 | // import { MnemonicKey } from '../../key'; 3 | // import { LCDClient } from './LCDClient'; 4 | // import { TreasuryAPI, Broadcast } from './api'; 5 | 6 | describe('Wallet', () => { 7 | it('sends a MsgSwap transaction', async () => { 8 | // const terra = new LCDClient({ 9 | // URL: 'https://soju-lcd.terra.dev', 10 | // chainID: 'soju-0014', 11 | // }); 12 | // const mk = new MnemonicKey({ 13 | // mnemonic: 14 | // 'sound hour era feature bacon code drift deal raw toward soldier nation winter consider tissue jewel script result mean faculty water exist lunch betray', 15 | // }); 16 | // const swap = new MsgSwap(mk.accAddress, new Coin('uluna', 1000), 'umnt'); 17 | // const wallet = terra.wallet(mk); 18 | // const a = await wallet.createAndSignTx({ 19 | // msgs: [swap], 20 | // memo: 'Hello World!', 21 | // fee: new StdFee(100000, { uluna: 10000 }), 22 | // }); 23 | // const b = await terra.tx.broadcast(a); 24 | // expect(b.height).toBeGreaterThan(0); 25 | // console.log(b.txhash); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /integration-tests/send.ts: -------------------------------------------------------------------------------- 1 | import { LCDClient, MsgSend, MnemonicKey } from '../src'; 2 | 3 | // create a key out of a mnemonic 4 | const mk = new MnemonicKey({ 5 | mnemonic: 6 | 'notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius', 7 | }); 8 | 9 | // To use LocalTerra 10 | const terra = new LCDClient({ 11 | URL: 'http://localhost:1317', 12 | chainID: 'testnet', 13 | gasPrices: '169.77ukrw', 14 | }); 15 | 16 | // a wallet can be created out of any key 17 | // wallets abstract transaction building 18 | const wallet = terra.wallet(mk); 19 | 20 | // create a simple message that moves coin balances 21 | const send = new MsgSend( 22 | 'terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v', 23 | 'terra17lmam6zguazs5q5u6z5mmx76uj63gldnse2pdp', 24 | { uusd: 1312029 } 25 | ); 26 | 27 | wallet 28 | .createAndSignTx({ 29 | msgs: [send], 30 | memo: 'test from terra.js!', 31 | timeout_height: 14500, 32 | }) 33 | .then(tx => terra.tx.broadcast(tx)) 34 | .then(result => { 35 | console.log(`TX raw_log: ${result.raw_log}`); 36 | console.log(`TX hash: ${result.txhash}`); 37 | }); 38 | -------------------------------------------------------------------------------- /src/core/StdSignature.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from './PublicKey'; 2 | import { JSONSerializable } from '../util/json'; 3 | 4 | /** 5 | * A signature consists of a message signature with a public key to verify its validity. 6 | * You likely will not need to work with StdSignature objects directly as they are automatically created for you. 7 | */ 8 | export class StdSignature extends JSONSerializable { 9 | /** 10 | * 11 | * @param signature Message signature string (base64-encoded). 12 | * @param pub_key Public key 13 | */ 14 | constructor(public signature: string, public pub_key: PublicKey) { 15 | super(); 16 | } 17 | 18 | public static fromData(data: StdSignature.Data): StdSignature { 19 | const { signature, pub_key } = data; 20 | return new StdSignature(signature, PublicKey.fromData(pub_key)); 21 | } 22 | 23 | public toData(): StdSignature.Data { 24 | const { signature, pub_key } = this; 25 | return { 26 | signature, 27 | pub_key: pub_key.toData(), 28 | }; 29 | } 30 | } 31 | export namespace StdSignature { 32 | export interface Data { 33 | signature: string; 34 | pub_key: PublicKey.Data; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/core/wasm/msgs/MsgClearContractAdmin.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | 4 | export class MsgClearContractAdmin extends JSONSerializable { 5 | /** 6 | * @param admin contract admin 7 | * @param new_admin new admin 8 | * @param contract contract address 9 | */ 10 | constructor(public admin: AccAddress, public contract: AccAddress) { 11 | super(); 12 | } 13 | 14 | public static fromData( 15 | data: MsgClearContractAdmin.Data 16 | ): MsgClearContractAdmin { 17 | const { 18 | value: { admin, contract }, 19 | } = data; 20 | return new MsgClearContractAdmin(admin, contract); 21 | } 22 | 23 | public toData(): MsgClearContractAdmin.Data { 24 | const { admin, contract } = this; 25 | return { 26 | type: 'wasm/MsgClearContractAdmin', 27 | value: { 28 | admin, 29 | contract, 30 | }, 31 | }; 32 | } 33 | } 34 | 35 | export namespace MsgClearContractAdmin { 36 | export interface Data { 37 | type: 'wasm/MsgClearContractAdmin'; 38 | value: { 39 | admin: AccAddress; 40 | contract: AccAddress; 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/client/lcd/APIRequester.ts: -------------------------------------------------------------------------------- 1 | import Axios, { AxiosInstance } from 'axios'; 2 | 3 | export interface APIResponse { 4 | height: string; 5 | result: T; 6 | } 7 | 8 | export type APIParams = Record; 9 | 10 | export class APIRequester { 11 | private axios: AxiosInstance; 12 | constructor(baseURL: string) { 13 | this.axios = Axios.create({ 14 | baseURL, 15 | headers: { 16 | Accept: 'application/json', 17 | }, 18 | timeout: 30000, 19 | }); 20 | } 21 | 22 | public async getRaw(endpoint: string, params: APIParams = {}): Promise { 23 | return this.axios.get(endpoint, { params }).then(d => d.data); 24 | } 25 | 26 | public async get( 27 | endpoint: string, 28 | params: APIParams = {} 29 | ): Promise> { 30 | return this.axios.get(endpoint, { params }).then(d => d.data); 31 | } 32 | 33 | public async postRaw(endpoint: string, data?: any): Promise { 34 | return this.axios.post(endpoint, data).then(d => d.data); 35 | } 36 | 37 | public async post(endpoint: string, data?: any): Promise> { 38 | return this.axios.post(endpoint, data).then(d => d.data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/core/wasm/params.ts: -------------------------------------------------------------------------------- 1 | import { ParamChange } from '../params/ParamChange'; 2 | import { Convert } from '../../util/convert'; 3 | 4 | type MaxContractSize = ParamChange.Type<'wasm', 'MaxContractSize', number>; 5 | 6 | type MaxContractGas = ParamChange.Type<'wasm', 'MaxContractGas', number>; 7 | 8 | type MaxContractMsgSize = ParamChange.Type< 9 | 'wasm', 10 | 'MaxContractMsgSize', 11 | number 12 | >; 13 | 14 | export type WasmParamChange = 15 | | MaxContractSize 16 | | MaxContractGas 17 | | MaxContractMsgSize; 18 | 19 | export namespace WasmParamChange { 20 | export type Data = 21 | | ParamChange.Data.Type 22 | | ParamChange.Data.Type 23 | | ParamChange.Data.Type; 24 | } 25 | 26 | export interface WasmParamChanges { 27 | wasm?: { 28 | MaxContractSize?: number; 29 | MaxContractGas?: number; 30 | MaxContractMsgSize?: number; 31 | }; 32 | } 33 | 34 | export namespace WasmParamChanges { 35 | export const ConversionTable = { 36 | wasm: { 37 | MaxContractSize: [Convert.toNumber, Convert.toFixed], 38 | MaxContractGas: [Convert.toNumber, Convert.toFixed], 39 | MaxContractMsgSize: [Convert.toNumber, Convert.toFixed], 40 | }, 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /src/core/msgauth/msgs/MsgExecAuthorized.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | import { Msg } from '../../Msg'; 4 | 5 | export class MsgExecAuthorized extends JSONSerializable { 6 | /** 7 | * @param grantee authorization grantee 8 | * @param msgs list of messages to execute 9 | */ 10 | constructor(public grantee: AccAddress, public msgs: Msg[]) { 11 | super(); 12 | } 13 | 14 | public static fromData(data: MsgExecAuthorized.Data): MsgExecAuthorized { 15 | const { 16 | value: { grantee, msgs }, 17 | } = data; 18 | return new MsgExecAuthorized( 19 | grantee, 20 | msgs.map(x => Msg.fromData(x)) 21 | ); 22 | } 23 | 24 | public toData(): MsgExecAuthorized.Data { 25 | const { grantee, msgs } = this; 26 | return { 27 | type: 'msgauth/MsgExecAuthorized', 28 | value: { 29 | grantee, 30 | msgs: msgs.map(msg => msg.toData()), 31 | }, 32 | }; 33 | } 34 | } 35 | 36 | export namespace MsgExecAuthorized { 37 | export interface Data { 38 | type: 'msgauth/MsgExecAuthorized'; 39 | value: { 40 | grantee: AccAddress; 41 | msgs: Msg.Data[]; 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/client/lcd/api/AuthAPI.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AccAddress, 3 | Account, 4 | Coins, 5 | LazyGradedVestingAccount, 6 | } from '../../../core'; 7 | import { BaseAPI } from './BaseAPI'; 8 | import { APIParams } from '../APIRequester'; 9 | 10 | export class AuthAPI extends BaseAPI { 11 | /** 12 | * Looks up the account information using its Terra account address. If the account has 13 | * vesting, it will be a [[LazyGradedVestingAccount]]. 14 | * 15 | * @param address address of account to look up 16 | */ 17 | public async accountInfo( 18 | address: AccAddress, 19 | params: APIParams = {} 20 | ): Promise { 21 | const { result } = await this.c.get< 22 | Account.Data | LazyGradedVestingAccount.Data 23 | >(`/auth/accounts/${address}`, params); 24 | 25 | // Until columbus-4 it used to return coins from /auth/accounts 26 | if (!result.value.coins) { 27 | result.value.coins = ( 28 | await this.c.get(`/bank/balances/${address}`, params) 29 | ).result; 30 | } 31 | 32 | if (result.type === 'core/Account') { 33 | return Account.fromData(result); 34 | } else { 35 | return LazyGradedVestingAccount.fromData(result); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/client/lcd/api/MsgAuthAPI.ts: -------------------------------------------------------------------------------- 1 | import { AccAddress } from '../../../core'; 2 | import { BaseAPI } from './BaseAPI'; 3 | import { AuthorizationGrant } from '../../../core/msgauth/Authorization'; 4 | import { APIParams } from '../APIRequester'; 5 | 6 | export class MsgAuthAPI extends BaseAPI { 7 | /** 8 | * Get the message authorization grants for a specific granter and grantee 9 | */ 10 | public async grants( 11 | granter: AccAddress, 12 | grantee: AccAddress, 13 | msgType?: string, 14 | params: APIParams = {} 15 | ): Promise { 16 | if (msgType === undefined) { 17 | return this.c 18 | .get( 19 | `/msgauth/granters/${granter}/grantees/${grantee}/grants`, 20 | params 21 | ) 22 | .then(d => d.result.map(AuthorizationGrant.fromData)); 23 | } else { 24 | return this.c 25 | .get( 26 | `/msgauth/granters/${granter}/grantees/${grantee}/grants/${msgType}`, 27 | params 28 | ) 29 | .then(d => { 30 | if (d.result === null) { 31 | return []; 32 | } 33 | return [AuthorizationGrant.fromData(d.result)]; 34 | }); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/core/market/params.ts: -------------------------------------------------------------------------------- 1 | import { ParamChange } from '../params/ParamChange'; 2 | import { Convert } from '../../util/convert'; 3 | import { Dec } from '../numeric'; 4 | 5 | type PoolRecoveryPeriod = ParamChange.Type< 6 | 'market', 7 | 'PoolRecoveryPeriod', 8 | number 9 | >; 10 | 11 | type BasePool = ParamChange.Type<'market', 'BasePool', Dec>; 12 | 13 | type MinStabilitySpread = ParamChange.Type<'market', 'MinStabilitySpread', Dec>; 14 | 15 | export type MarketParamChange = 16 | | PoolRecoveryPeriod 17 | | BasePool 18 | | MinStabilitySpread; 19 | 20 | export namespace MarketParamChange { 21 | export type Data = 22 | | ParamChange.Data.Type 23 | | ParamChange.Data.Type 24 | | ParamChange.Data.Type; 25 | } 26 | 27 | export interface MarketParamChanges { 28 | market?: { 29 | PoolRecoveryPeriod?: number; 30 | BasePool?: Dec; 31 | MinStabilitySpread?: Dec; 32 | }; 33 | } 34 | 35 | export namespace MarketParamChanges { 36 | export const ConversionTable = { 37 | market: { 38 | PoolRecoveryPeriod: [Convert.toNumber, Convert.toFixed], 39 | BasePool: [Convert.toDec, Convert.toString], 40 | MinStabilitySpread: [Convert.toDec, Convert.toString], 41 | }, 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /src/core/gov/proposals/TextProposal.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | 3 | /** 4 | * Basic proposal which describes the candidate proposition that must be put into effect 5 | * manually if passed. Used as a general-purpose way of discovering community's 6 | * sentiment / interest for an arbitrary change. 7 | */ 8 | export class TextProposal extends JSONSerializable { 9 | /** 10 | * @param title proposal's title 11 | * @param description proposal's description 12 | */ 13 | constructor(public title: string, public description: string) { 14 | super(); 15 | } 16 | 17 | public static fromData(data: TextProposal.Data): TextProposal { 18 | const { 19 | value: { title, description }, 20 | } = data; 21 | return new TextProposal(title, description); 22 | } 23 | 24 | public toData(): TextProposal.Data { 25 | const { title, description } = this; 26 | return { 27 | type: 'gov/TextProposal', 28 | value: { 29 | title, 30 | description, 31 | }, 32 | }; 33 | } 34 | } 35 | 36 | export namespace TextProposal { 37 | export interface Data { 38 | type: 'gov/TextProposal'; 39 | value: { 40 | title: string; 41 | description: string; 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/core/treasury/PolicyConstraints.spec.ts: -------------------------------------------------------------------------------- 1 | import { PolicyConstraints } from './PolicyConstraints'; 2 | import { Coin } from '../Coin'; 3 | import { Dec } from '../numeric'; 4 | 5 | describe('PolicyConstraints', () => { 6 | it('clamp', () => { 7 | const pc = new PolicyConstraints( 8 | Dec.withPrec(5, 4), 9 | Dec.withPrec(1, 2), 10 | new Coin('usdr', 1), 11 | Dec.withPrec(25, 5) 12 | ); 13 | 14 | const tr = Dec.withPrec(1, 3); 15 | 16 | let prevRate = tr; 17 | let newRate = prevRate.add(pc.change_rate_max.mul(2)); 18 | let clampedRate = pc.clamp(prevRate, newRate); 19 | expect(prevRate.add(pc.change_rate_max)).toEqual(clampedRate); 20 | 21 | prevRate = tr; 22 | newRate = prevRate.sub(pc.change_rate_max.mul(2)); 23 | clampedRate = pc.clamp(prevRate, newRate); 24 | expect(prevRate.sub(pc.change_rate_max)).toEqual(clampedRate); 25 | 26 | prevRate = pc.rate_max; 27 | newRate = prevRate.add(Dec.withPrec(1, 3)); 28 | clampedRate = pc.clamp(prevRate, newRate); 29 | expect(pc.rate_max).toEqual(clampedRate); 30 | 31 | prevRate = pc.rate_min; 32 | newRate = pc.rate_min.sub(Dec.withPrec(1, 3)); 33 | clampedRate = pc.clamp(prevRate, newRate); 34 | expect(pc.rate_min).toEqual(clampedRate); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/core/wasm/msgs/index.ts: -------------------------------------------------------------------------------- 1 | import { MsgStoreCode } from './MsgStoreCode'; 2 | import { MsgMigrateCode } from './MsgMigrateCode'; 3 | import { MsgInstantiateContract } from './MsgInstantiateContract'; 4 | import { MsgExecuteContract } from './MsgExecuteContract'; 5 | import { MsgMigrateContract } from './MsgMigrateContract'; 6 | import { MsgUpdateContractAdmin } from './MsgUpdateContractAdmin'; 7 | import { MsgClearContractAdmin } from './MsgClearContractAdmin'; 8 | 9 | export * from './MsgStoreCode'; 10 | export * from './MsgMigrateCode'; 11 | export * from './MsgInstantiateContract'; 12 | export * from './MsgExecuteContract'; 13 | export * from './MsgMigrateContract'; 14 | export * from './MsgUpdateContractAdmin'; 15 | export * from './MsgClearContractAdmin'; 16 | 17 | export type WasmMsg = 18 | | MsgStoreCode 19 | | MsgMigrateCode 20 | | MsgInstantiateContract 21 | | MsgExecuteContract 22 | | MsgMigrateContract 23 | | MsgUpdateContractAdmin 24 | | MsgClearContractAdmin; 25 | 26 | export namespace WasmMsg { 27 | export type Data = 28 | | MsgStoreCode.Data 29 | | MsgMigrateCode.Data 30 | | MsgInstantiateContract.Data 31 | | MsgExecuteContract.Data 32 | | MsgMigrateContract.Data 33 | | MsgUpdateContractAdmin.Data 34 | | MsgClearContractAdmin.Data; 35 | } 36 | -------------------------------------------------------------------------------- /integration-tests/executeContract.ts: -------------------------------------------------------------------------------- 1 | import { LCDClient, MnemonicKey, MsgExecuteContract, Wallet } from '../src'; 2 | 3 | const client = new LCDClient({ 4 | chainID: 'bombay-9', 5 | URL: 'https://bombay-lcd.terra.dev', 6 | }); 7 | 8 | // LocalTerra test1 terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v 9 | const mk = new MnemonicKey({ 10 | mnemonic: 11 | 'notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius', 12 | }); 13 | 14 | const wallet = client.wallet(mk); 15 | 16 | async function main() { 17 | const execute = new MsgExecuteContract( 18 | wallet.key.accAddress, // sender 19 | 'terra156v8s539wtz0sjpn8y8a8lfg8fhmwa7fy22aff', // contract account address 20 | // handle msg 21 | { 22 | swap: { 23 | offer_asset: { 24 | amount: '1000000', 25 | info: { 26 | native_token: { 27 | denom: 'uluna', 28 | }, 29 | }, 30 | }, 31 | }, 32 | }, 33 | { uluna: 1000000 } // coins 34 | ); 35 | 36 | const executeTx = await wallet.createAndSignTx({ 37 | msgs: [execute], 38 | }); 39 | 40 | const executeTxResult = await client.tx.broadcastSync(executeTx); 41 | console.log(executeTxResult); 42 | } 43 | 44 | main().catch(console.error); 45 | -------------------------------------------------------------------------------- /src/core/StdFee.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../util/json'; 2 | import { Coins } from './Coins'; 3 | import { Int } from './numeric'; 4 | 5 | /** 6 | * A transaction must include a fee, otherwise it will be rejected. 7 | */ 8 | export class StdFee extends JSONSerializable { 9 | /** Fee amount to be paid */ 10 | public readonly amount: Coins; 11 | 12 | /** 13 | * Creates a new StdFee object. 14 | * @param gas gas limit 15 | * @param amount amount to be paid to validator 16 | */ 17 | constructor(public readonly gas: number, amount: Coins.Input) { 18 | super(); 19 | this.amount = new Coins(amount); 20 | } 21 | 22 | public static fromData(data: StdFee.Data): StdFee { 23 | const { gas, amount } = data; 24 | return new StdFee(Number.parseInt(gas), Coins.fromData(amount)); 25 | } 26 | 27 | public toData(): StdFee.Data { 28 | return { 29 | gas: new Int(this.gas).toString(), 30 | amount: this.amount.toData(), 31 | }; 32 | } 33 | 34 | /** 35 | * Gets the mininimum gas prices implied by the fee. Minimum gas prices are `fee amount / gas`. 36 | */ 37 | public gasPrices(): Coins { 38 | return this.amount.toDecCoins().div(this.gas); 39 | } 40 | } 41 | 42 | export namespace StdFee { 43 | export interface Data { 44 | gas: string; 45 | amount: Coins.Data; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/core/Deposit.ts: -------------------------------------------------------------------------------- 1 | import { Coins } from './Coins'; 2 | import { JSONSerializable } from '../util/json'; 3 | import { AccAddress } from './bech32'; 4 | 5 | /** 6 | * Stores deposit information for a proposal 7 | */ 8 | export class Deposit extends JSONSerializable { 9 | public amount: Coins; 10 | /** 11 | * @param proposal_id Id of porposal to deposit to 12 | * @param depositor depositor's account address 13 | * @param amount amount to deposit 14 | */ 15 | constructor( 16 | public proposal_id: number, 17 | public depositor: AccAddress, 18 | amount: Coins.Input 19 | ) { 20 | super(); 21 | this.amount = new Coins(amount); 22 | } 23 | 24 | public static fromData(data: Deposit.Data): Deposit { 25 | const { proposal_id, depositor, amount } = data; 26 | return new Deposit( 27 | Number.parseInt(proposal_id), 28 | depositor, 29 | Coins.fromData(amount) 30 | ); 31 | } 32 | 33 | public toData(): Deposit.Data { 34 | const { proposal_id, depositor, amount } = this; 35 | return { 36 | proposal_id: proposal_id.toString(), 37 | depositor, 38 | amount: amount.toData(), 39 | }; 40 | } 41 | } 42 | 43 | export namespace Deposit { 44 | export interface Data { 45 | proposal_id: string; 46 | depositor: AccAddress; 47 | amount: Coins.Data; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/core/msgauth/msgs/MsgGrantAuthorization.data.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "type": "msgauth/MsgGrantAuthorization", 3 | "value": { 4 | "granter": "terra1nty4ku4a79zj45jl0fy5rrjun07ny0nrve7j99", 5 | "grantee": "terra1qfqa2eu9wp272ha93lj4yhcenrc6ymng079nu8", 6 | "grant": { 7 | "authorization": { 8 | "type": "msgauth/SendAuthorization", 9 | "value": { 10 | "spend_limit": [{ 11 | "denom": "ukrw", 12 | "amount": "1000000" 13 | }] 14 | } 15 | }, 16 | "expiration": "2022-06-29T08:18:15Z" 17 | } 18 | } 19 | }, 20 | { 21 | "type": "msgauth/MsgGrantAuthorization", 22 | "value": { 23 | "granter": "terra1nty4ku4a79zj45jl0fy5rrjun07ny0nrve7j99", 24 | "grantee": "terra1qfqa2eu9wp272ha93lj4yhcenrc6ymng079nu8", 25 | "grant": { 26 | "authorization": { 27 | "type": "msgauth/GenericAuthorization", 28 | "value": { 29 | "grant_msg_type": "swap" 30 | } 31 | }, 32 | "expiration": "2022-06-29T08:18:15Z" 33 | } 34 | } 35 | } 36 | ] -------------------------------------------------------------------------------- /src/core/distribution/msgs/MsgFundCommunityPool.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | import { Coins } from '../../Coins'; 4 | 5 | export class MsgFundCommunityPool extends JSONSerializable { 6 | public amount: Coins; 7 | /** 8 | * @param depositor depositor's account address 9 | * @param amount coins to fund the community pool 10 | */ 11 | constructor(public depositor: AccAddress, amount: Coins.Input) { 12 | super(); 13 | this.amount = new Coins(amount); 14 | } 15 | 16 | public static fromData( 17 | data: MsgFundCommunityPool.Data 18 | ): MsgFundCommunityPool { 19 | const { 20 | value: { depositor, amount }, 21 | } = data; 22 | return new MsgFundCommunityPool(depositor, Coins.fromData(amount)); 23 | } 24 | 25 | public toData(): MsgFundCommunityPool.Data { 26 | const { depositor, amount } = this; 27 | return { 28 | type: 'distribution/MsgFundCommunityPool', 29 | value: { 30 | depositor, 31 | amount: amount.toData(), 32 | }, 33 | }; 34 | } 35 | } 36 | 37 | export namespace MsgFundCommunityPool { 38 | export interface Data { 39 | type: 'distribution/MsgFundCommunityPool'; 40 | value: { 41 | depositor: AccAddress; 42 | amount: Coins.Data; 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/core/slashing/msgs/MsgUnjail.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { ValAddress } from '../../bech32'; 3 | 4 | /** 5 | * A validator can be jailed by the blockchain if misbehavior is detected, such as 6 | * double-signing or having missed too many vote periods in the Oracle ballot. 7 | * 8 | * This is done to protect delegators' funds from getting slashed further, until the 9 | * validator's issues have been addressed. A jailed validator cannot participate in 10 | * block rewards, and must be manually unjailed by submitting this message. 11 | */ 12 | export class MsgUnjail extends JSONSerializable { 13 | /** 14 | * @param address validator's operator address 15 | */ 16 | constructor(public address: ValAddress) { 17 | super(); 18 | } 19 | 20 | public static fromData(data: MsgUnjail.Data): MsgUnjail { 21 | const { 22 | value: { address }, 23 | } = data; 24 | return new MsgUnjail(address); 25 | } 26 | 27 | public toData(): MsgUnjail.Data { 28 | const { address } = this; 29 | return { 30 | type: 'slashing/MsgUnjail', 31 | value: { 32 | address, 33 | }, 34 | }; 35 | } 36 | } 37 | 38 | export namespace MsgUnjail { 39 | export interface Data { 40 | type: 'slashing/MsgUnjail'; 41 | value: { 42 | address: ValAddress; 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/core/wasm/msgs/MsgUpdateContractAdmin.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | 4 | export class MsgUpdateContractAdmin extends JSONSerializable { 5 | /** 6 | * @param admin contract admin 7 | * @param new_admin new admin 8 | * @param contract contract address 9 | */ 10 | constructor( 11 | public admin: AccAddress, 12 | public new_admin: AccAddress, 13 | public contract: AccAddress 14 | ) { 15 | super(); 16 | } 17 | 18 | public static fromData( 19 | data: MsgUpdateContractAdmin.Data 20 | ): MsgUpdateContractAdmin { 21 | const { 22 | value: { admin, new_admin, contract }, 23 | } = data; 24 | return new MsgUpdateContractAdmin(admin, new_admin, contract); 25 | } 26 | 27 | public toData(): MsgUpdateContractAdmin.Data { 28 | const { admin, new_admin, contract } = this; 29 | return { 30 | type: 'wasm/MsgUpdateContractAdmin', 31 | value: { 32 | admin, 33 | new_admin, 34 | contract, 35 | }, 36 | }; 37 | } 38 | } 39 | 40 | export namespace MsgUpdateContractAdmin { 41 | export interface Data { 42 | type: 'wasm/MsgUpdateContractAdmin'; 43 | value: { 44 | admin: AccAddress; 45 | new_admin: AccAddress; 46 | contract: AccAddress; 47 | }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/client/lcd/Wallet.ts: -------------------------------------------------------------------------------- 1 | import { LCDClient } from './LCDClient'; 2 | import { Key } from '../../key'; 3 | import { CreateTxOptions } from '../lcd/api/TxAPI'; 4 | 5 | import { StdTx } from '../../core/StdTx'; 6 | import { StdSignMsg } from '../../core/StdSignMsg'; 7 | 8 | export class Wallet { 9 | constructor(public lcd: LCDClient, public key: Key) {} 10 | 11 | public accountNumberAndSequence(): Promise<{ 12 | account_number: number; 13 | sequence: number; 14 | }> { 15 | return this.lcd.auth.accountInfo(this.key.accAddress).then(d => { 16 | return { 17 | account_number: d.account_number, 18 | sequence: d.sequence, 19 | }; 20 | }); 21 | } 22 | 23 | public accountNumber(): Promise { 24 | return this.lcd.auth.accountInfo(this.key.accAddress).then(d => { 25 | return d.account_number; 26 | }); 27 | } 28 | 29 | public sequence(): Promise { 30 | return this.lcd.auth.accountInfo(this.key.accAddress).then(d => { 31 | return d.sequence; 32 | }); 33 | } 34 | 35 | public createTx(options: CreateTxOptions): Promise { 36 | return this.lcd.tx.create(this.key.accAddress, options); 37 | } 38 | 39 | public async createAndSignTx(options: CreateTxOptions): Promise { 40 | const stdSignMsg = await this.createTx(options); 41 | return this.key.signTx(stdSignMsg); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/core/wasm/msgs/MsgMigrateCode.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | 4 | export class MsgMigrateCode extends JSONSerializable { 5 | /** 6 | * @param sender code migrator address 7 | * @param code_id reference to the code on the blockchain 8 | * @param wasm_byte_code base64-encoded bytecode contents 9 | */ 10 | constructor( 11 | public sender: AccAddress, 12 | public code_id: number, 13 | public wasm_byte_code: string 14 | ) { 15 | super(); 16 | } 17 | 18 | public static fromData(data: MsgMigrateCode.Data): MsgMigrateCode { 19 | const { 20 | value: { sender, code_id, wasm_byte_code }, 21 | } = data; 22 | return new MsgMigrateCode(sender, Number.parseInt(code_id), wasm_byte_code); 23 | } 24 | 25 | public toData(): MsgMigrateCode.Data { 26 | const { sender, code_id, wasm_byte_code } = this; 27 | return { 28 | type: 'wasm/MsgMigrateCode', 29 | value: { 30 | sender, 31 | code_id: code_id.toFixed(), 32 | wasm_byte_code, 33 | }, 34 | }; 35 | } 36 | } 37 | 38 | export namespace MsgMigrateCode { 39 | export interface Data { 40 | type: 'wasm/MsgMigrateCode'; 41 | value: { 42 | sender: AccAddress; 43 | code_id: string; 44 | wasm_byte_code: string; 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/core/oracle/AggregateExchangeRatePrevote.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../util/json'; 2 | import { ValAddress } from '../bech32'; 3 | 4 | /** 5 | * Stores information about data about Oracle aggregate prevotes fetched from the blockchain. 6 | */ 7 | export class AggregateExchangeRatePrevote extends JSONSerializable { 8 | /** 9 | * @param hash aggregate vote hash 10 | * @param voter validator 11 | * @param submit_block block during which aggregate prevote was submitted 12 | */ 13 | constructor( 14 | public hash: string, 15 | public voter: ValAddress, 16 | public submit_block: number 17 | ) { 18 | super(); 19 | } 20 | 21 | public static fromData( 22 | data: AggregateExchangeRatePrevote.Data 23 | ): AggregateExchangeRatePrevote { 24 | const { hash, voter, submit_block } = data; 25 | return new AggregateExchangeRatePrevote( 26 | hash, 27 | voter, 28 | Number.parseInt(submit_block) 29 | ); 30 | } 31 | 32 | public toData(): AggregateExchangeRatePrevote.Data { 33 | const { hash, voter, submit_block } = this; 34 | return { 35 | hash, 36 | voter, 37 | submit_block: submit_block.toFixed(), 38 | }; 39 | } 40 | } 41 | 42 | export namespace AggregateExchangeRatePrevote { 43 | export interface Data { 44 | hash: string; 45 | voter: ValAddress; 46 | submit_block: string; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/util/hash.spec.ts: -------------------------------------------------------------------------------- 1 | import { hashAmino } from './hash'; 2 | 3 | describe('hashAmino', () => { 4 | it('hashes correctly', () => { 5 | expect( 6 | hashAmino( 7 | '2gbGwQI/ClDeU+EsChIyNjc4NzI5ODA2ODA4OTgxNTQSBDlkYmMaBHV1c2QiFFvGfiBRs1FeYdwYQmKwm3W+3+TNKhSwR4zq2Hn7r74oEK5S3SHokyzgSQpT3lPhLAoVMzExMDA1ODg4MDMwMTM2MzYxNTIyEgQzN2NjGgR1a3J3IhRbxn4gUbNRXmHcGEJisJt1vt/kzSoUsEeM6th5+6++KBCuUt0h6JMs4EkKUN5T4SwKEjE5NDQzNzc0NjAxMTA3NTM4NRIEYTBhMhoEdXNkciIUW8Z+IFGzUV5h3BhCYrCbdb7f5M0qFLBHjOrYefuvvigQrlLdIeiTLOBJClPeU+EsChU3Mjk5NTM4NzIzNTU0NDc1MTU0NzQSBGMxNzgaBHVtbnQiFFvGfiBRs1FeYdwYQmKwm3W+3+TNKhSwR4zq2Hn7r74oEK5S3SHokyzgSQpgcVjB3gooYTU3MjBkNWE0NjJiN2Y4NjdhZjc1NWJmYzNmZTM1NjI3YzM3MzgyYhIEdXVzZBoUW8Z+IFGzUV5h3BhCYrCbdb7f5M0iFLBHjOrYefuvvigQrlLdIeiTLOBJCmBxWMHeCig4NDM4ZGY5MWIyZTRjY2IzNzg0Y2FiZDRlODRjZjIwZjQxODAwOTEwEgR1a3J3GhRbxn4gUbNRXmHcGEJisJt1vt/kzSIUsEeM6th5+6++KBCuUt0h6JMs4EkKYHFYwd4KKDRjMjA5ODFlMGM2NjA5OGVlMzlmMGU0YWZiYmFmYmMwNTEzNmUxYmESBHVzZHIaFFvGfiBRs1FeYdwYQmKwm3W+3+TNIhSwR4zq2Hn7r74oEK5S3SHokyzgSQpgcVjB3gooYmIxNTE5NDdjYWQxYTBhYmNlOWNiNzgwYTVmYWE1MWQwYjdmOWY1MBIEdW1udBoUW8Z+IFGzUV5h3BhCYrCbdb7f5M0iFLBHjOrYefuvvigQrlLdIeiTLOBJEhIKDAoEdWtydxIENDAwMBDAmgwaagom61rphyEDhEN2CNA81Z+AmzAQE1jn2oSTodQUbDXsn2Hu38eFQJYSQJEFGtcrE5JUrEDxfOQTBjbPQKMN0nvqpkC1hbqkBLeUCVpal4LI+IwyQDHF1NwQCRgB0k0MeoD4ZkzzAh6igws=' 8 | ) 9 | ).toEqual( 10 | 'E2969CFC525D852234EAF30F7F2A71ACC1A89261543E0343220FD4030F58A140' 11 | ); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/core/distribution/msgs/MsgWithdrawValidatorCommission.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { ValAddress } from '../../bech32'; 3 | 4 | /** 5 | * A validator can withdraw their outstanding commission rewards accrued from all 6 | * delegations (not including its self-delegation) into their associated account's 7 | * withdraw address. 8 | */ 9 | export class MsgWithdrawValidatorCommission extends JSONSerializable { 10 | /** 11 | * @param validator_address validator's operator address 12 | */ 13 | constructor(public validator_address: ValAddress) { 14 | super(); 15 | } 16 | 17 | public static fromData( 18 | data: MsgWithdrawValidatorCommission.Data 19 | ): MsgWithdrawValidatorCommission { 20 | const { 21 | value: { validator_address }, 22 | } = data; 23 | return new MsgWithdrawValidatorCommission(validator_address); 24 | } 25 | 26 | public toData(): MsgWithdrawValidatorCommission.Data { 27 | const { validator_address } = this; 28 | return { 29 | type: 'distribution/MsgWithdrawValidatorCommission', 30 | value: { 31 | validator_address, 32 | }, 33 | }; 34 | } 35 | } 36 | 37 | export namespace MsgWithdrawValidatorCommission { 38 | export interface Data { 39 | type: 'distribution/MsgWithdrawValidatorCommission'; 40 | value: { 41 | validator_address: ValAddress; 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/core/msgauth/msgs/MsgRevokeAuthorization.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | 4 | export class MsgRevokeAuthorization extends JSONSerializable { 5 | /** 6 | * @param granter authorization granter 7 | * @param grantee authorization grantee 8 | * @param authorization_msg_type type of message to revoke 9 | */ 10 | constructor( 11 | public granter: AccAddress, 12 | public grantee: AccAddress, 13 | public msg_type_url: string 14 | ) { 15 | super(); 16 | } 17 | 18 | public static fromData( 19 | data: MsgRevokeAuthorization.Data 20 | ): MsgRevokeAuthorization { 21 | const { 22 | value: { granter, grantee, msg_type_url }, 23 | } = data; 24 | return new MsgRevokeAuthorization(granter, grantee, msg_type_url); 25 | } 26 | 27 | public toData(): MsgRevokeAuthorization.Data { 28 | const { granter, grantee, msg_type_url } = this; 29 | return { 30 | type: 'msgauth/MsgRevokeAuthorization', 31 | value: { 32 | granter, 33 | grantee, 34 | msg_type_url, 35 | }, 36 | }; 37 | } 38 | } 39 | 40 | export namespace MsgRevokeAuthorization { 41 | export interface Data { 42 | type: 'msgauth/MsgRevokeAuthorization'; 43 | value: { 44 | granter: AccAddress; 45 | grantee: AccAddress; 46 | msg_type_url: string; 47 | }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/core/staking/Delegation.data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "delegation": { 4 | "delegator_address": "terra1rk6tvacasnnyssfnn00zl7wz43pjnpn7vayqv6", 5 | "validator_address": "terravaloper1vze7n65ccq08auu2xwmptlymu3gdlx0z5j033m", 6 | "shares": "0.362811214041104489" 7 | }, 8 | "balance": { 9 | "denom": "uluna", 10 | "amount": "0" 11 | } 12 | }, 13 | { 14 | "delegation": { 15 | "delegator_address": "terra1rk6tvacasnnyssfnn00zl7wz43pjnpn7vayqv6", 16 | "validator_address": "terravaloper1062zkmnlqhcpwsryk5kjf345x6njzzksqk9ljl", 17 | "shares": "1041435300584.646580892301867080" 18 | }, 19 | "balance": { 20 | "denom": "uluna", 21 | "amount": "970201986983" 22 | } 23 | }, 24 | { 25 | "delegation": { 26 | "delegator_address": "terra1rk6tvacasnnyssfnn00zl7wz43pjnpn7vayqv6", 27 | "validator_address": "terravaloper14un92kpq0pvflyjd8sxmrsswglhn8mf5hkruc2", 28 | "shares": "0.317889676069212335" 29 | }, 30 | "balance": { 31 | "denom": "uluna", 32 | "amount": "0" 33 | } 34 | }, 35 | { 36 | "delegation": { 37 | "delegator_address": "terra1rk6tvacasnnyssfnn00zl7wz43pjnpn7vayqv6", 38 | "validator_address": "terravaloper1kcux5ht2jslsyfzgs5wtfg4hx9dha8z4tes7xh", 39 | "shares": "937199434798.716278082038875955" 40 | }, 41 | "balance": { 42 | "denom": "uluna", 43 | "amount": "891000004719" 44 | } 45 | } 46 | ] -------------------------------------------------------------------------------- /src/core/gov/msgs/MsgVote.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | 4 | /** 5 | * Vote for a proposal 6 | */ 7 | export class MsgVote extends JSONSerializable { 8 | /** 9 | * @param proposal_id ID of proposal to vote on 10 | * @param voter voter's account address 11 | * @param option one of voting options 12 | */ 13 | constructor( 14 | public proposal_id: number, 15 | public voter: AccAddress, 16 | public option: MsgVote.Option 17 | ) { 18 | super(); 19 | } 20 | 21 | public static fromData(data: MsgVote.Data): MsgVote { 22 | const { 23 | value: { proposal_id, voter, option }, 24 | } = data; 25 | return new MsgVote(Number.parseInt(proposal_id), voter, option); 26 | } 27 | 28 | public toData(): MsgVote.Data { 29 | const { proposal_id, voter, option } = this; 30 | return { 31 | type: 'gov/MsgVote', 32 | value: { 33 | proposal_id: proposal_id.toFixed(), 34 | voter, 35 | option, 36 | }, 37 | }; 38 | } 39 | } 40 | 41 | export namespace MsgVote { 42 | export enum Option { 43 | EMPTY = 0, 44 | YES = 1, 45 | ABSTAIN = 2, 46 | NO = 3, 47 | NO_WITH_VETO = 4, 48 | } 49 | 50 | export interface Data { 51 | type: 'gov/MsgVote'; 52 | value: { 53 | proposal_id: string; 54 | voter: AccAddress; 55 | option: Option; 56 | }; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/core/staking/params.ts: -------------------------------------------------------------------------------- 1 | import { Denom } from '../Denom'; 2 | import { ParamChange } from '../params/ParamChange'; 3 | import { Convert } from '../../util/convert'; 4 | 5 | type UnbondingTime = ParamChange.Type<'staking', 'UnbondingTime', number>; 6 | 7 | type MaxValidators = ParamChange.Type<'staking', 'MaxValidators', number>; 8 | 9 | type MaxEntries = ParamChange.Type< 10 | 'staking', 11 | 'KeyMaxEntries', // lol wtf 12 | number 13 | >; 14 | 15 | type BondDenom = ParamChange.Type<'staking', 'BondDenom', Denom>; 16 | 17 | export type StakingParamChange = 18 | | UnbondingTime 19 | | MaxValidators 20 | | MaxEntries 21 | | BondDenom; 22 | 23 | export namespace StakingParamChange { 24 | export type Data = 25 | | ParamChange.Data.Type 26 | | ParamChange.Data.Type 27 | | ParamChange.Data.Type 28 | | ParamChange.Data.Type; 29 | } 30 | 31 | export interface StakingParamChanges { 32 | staking?: { 33 | UnbondingTime?: number; 34 | MaxValidators?: number; 35 | KeyMaxEntries?: number; 36 | BondDenom?: Denom; 37 | }; 38 | } 39 | 40 | export namespace StakingParamChanges { 41 | export const ConversionTable = { 42 | staking: { 43 | UnbondingTime: [Convert.toNumber, Convert.toFixed], 44 | MaxValidators: [Convert.toNumber, Convert.toNumber], 45 | KeyMaxEntries: [Convert.toNumber, Convert.toNumber], 46 | BondDenom: [Convert.id, Convert.id], 47 | }, 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /src/core/Block.ts: -------------------------------------------------------------------------------- 1 | export interface BlockInfo { 2 | block_id: BlockID; 3 | block: Block; 4 | } 5 | 6 | export interface Block { 7 | header: Header; 8 | data: Data; 9 | evidence: Evidence; 10 | last_commit: LastCommit; 11 | } 12 | 13 | export interface Data { 14 | txs: string[] | null; 15 | } 16 | 17 | export interface Evidence { 18 | evidence: string | null; 19 | } 20 | 21 | export interface Header { 22 | version: Version; 23 | 24 | /** blockchain ID */ 25 | chain_id: string; 26 | 27 | /** block's height */ 28 | height: string; 29 | 30 | /** time the block was included */ 31 | time: string; 32 | last_block_id: BlockID; 33 | last_commit_hash: string; 34 | data_hash: string; 35 | validators_hash: string; 36 | next_validators_hash: string; 37 | consensus_hash: string; 38 | app_hash: string; 39 | last_results_hash: string; 40 | evidence_hash: string; 41 | proposer_address: string; 42 | } 43 | 44 | export interface BlockID { 45 | hash: string; 46 | parts: Parts; 47 | } 48 | 49 | export interface Parts { 50 | total: string; 51 | hash: string; 52 | } 53 | 54 | export interface Version { 55 | block: string; 56 | app: string; 57 | } 58 | 59 | export interface LastCommit { 60 | height: string; 61 | round: string; 62 | block_id: BlockID; 63 | signatures: Signature[]; 64 | } 65 | 66 | export interface Signature { 67 | block_id_flag: number; 68 | validator_address: string; 69 | timestamp: string; 70 | signature: string; 71 | } 72 | -------------------------------------------------------------------------------- /integration-tests/estimateFee.ts: -------------------------------------------------------------------------------- 1 | import { LCDClient, LocalTerra, MsgSwap, MsgSend, Coin, StdTx, StdFee } from '../src'; 2 | import Axios from 'axios'; 3 | 4 | const lt = new LocalTerra(); 5 | const test1 = lt.wallets.test1; 6 | const test2 = lt.wallets.test2; 7 | 8 | async function main() { 9 | const { data: gasPrices } = await Axios.get( 10 | 'https://bombay-fcd.terra.dev/v1/txs/gas_prices' 11 | ); 12 | 13 | const tequila = new LCDClient({ 14 | chainID: 'bombay-9', 15 | URL: 'https://bombay-lcd.terra.dev', 16 | gasPrices, 17 | }); 18 | 19 | // Test raw estimate fee function with specified gas 20 | const rawFee = await tequila.tx.estimateFee( 21 | test1.key.accAddress, 22 | [new MsgSwap(test1.key.accAddress, new Coin('uluna', 1000), 'uusd')], 23 | { gas: '500000' } 24 | ); 25 | 26 | console.log('MsgSwap(500000 gas): ', rawFee.toJSON()); 27 | 28 | // Test automatic fee estimation using create method with specified denom 29 | const item = await tequila.tx.create(test1.key.accAddress, { 30 | msgs: [new MsgSwap(test1.key.accAddress, new Coin('uluna', 1000), 'uusd')], 31 | feeDenoms: ['uusd'], 32 | }); 33 | 34 | console.log('MsgSwap(uusd fee)', item.fee.toJSON()); 35 | 36 | const send = await tequila.tx.create(test2.key.accAddress, { 37 | msgs: [new MsgSend(test2.key.accAddress, test1.key.accAddress, '1234uusd')], 38 | feeDenoms: ['uusd'], 39 | }); 40 | 41 | console.log('MsgSend', send.fee.toJSON()); 42 | } 43 | 44 | main().catch(console.error); 45 | -------------------------------------------------------------------------------- /src/core/PublicKey.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../util/json'; 2 | 3 | export class PublicKey extends JSONSerializable { 4 | constructor( 5 | public readonly type: PublicKey.Data['type'], 6 | public readonly value: PublicKey.Data['value'] 7 | ) { 8 | super(); 9 | } 10 | 11 | public static fromData(data: PublicKey.Data): PublicKey { 12 | const { type, value } = data; 13 | return new PublicKey(type, value); 14 | } 15 | 16 | public toData(): PublicKey.Data { 17 | const { type, value } = this; 18 | if (type === 'tendermint/PubKeySecp256k1' && typeof value === 'string') { 19 | return { 20 | type, 21 | value, 22 | }; 23 | } else if ( 24 | type === 'tendermint/PubKeyMultisigThreshold' && 25 | typeof value !== 'string' 26 | ) { 27 | return { 28 | type, 29 | value, 30 | }; 31 | } 32 | throw new TypeError('invalid public key: type and value do not match'); 33 | } 34 | } 35 | 36 | export namespace PublicKey { 37 | export type Data = SimplePublicKey.Data | MultisigPublicKey.Data; 38 | } 39 | 40 | export namespace SimplePublicKey { 41 | export interface Data { 42 | type: 'tendermint/PubKeySecp256k1'; 43 | value: string; 44 | } 45 | } 46 | 47 | export namespace MultisigPublicKey { 48 | export interface Data { 49 | type: 'tendermint/PubKeyMultisigThreshold'; 50 | value: { 51 | threshold: string; 52 | pubkeys: SimplePublicKey.Data[]; 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/core/oracle/msgs/MsgAggregateExchangeRatePrevote.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress, ValAddress } from '../../bech32'; 3 | 4 | /** 5 | * Aggregate analog of MsgExchangeRatePrevote 6 | */ 7 | export class MsgAggregateExchangeRatePrevote extends JSONSerializable { 8 | /** 9 | * @param hash vote hash 10 | * @param feeder validator's feeder account address 11 | * @param validator validator's operator address 12 | */ 13 | constructor( 14 | public hash: string, 15 | public feeder: AccAddress, 16 | public validator: ValAddress 17 | ) { 18 | super(); 19 | } 20 | 21 | public static fromData( 22 | data: MsgAggregateExchangeRatePrevote.Data 23 | ): MsgAggregateExchangeRatePrevote { 24 | const { 25 | value: { hash, feeder, validator }, 26 | } = data; 27 | return new MsgAggregateExchangeRatePrevote(hash, feeder, validator); 28 | } 29 | 30 | public toData(): MsgAggregateExchangeRatePrevote.Data { 31 | const { hash, feeder, validator } = this; 32 | return { 33 | type: 'oracle/MsgAggregateExchangeRatePrevote', 34 | value: { 35 | hash, 36 | feeder, 37 | validator, 38 | }, 39 | }; 40 | } 41 | } 42 | 43 | export namespace MsgAggregateExchangeRatePrevote { 44 | export interface Data { 45 | type: 'oracle/MsgAggregateExchangeRatePrevote'; 46 | value: { 47 | hash: string; 48 | feeder: AccAddress; 49 | validator: ValAddress; 50 | }; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/core/bank/msgs/MsgSend.ts: -------------------------------------------------------------------------------- 1 | import { Coins } from '../../Coins'; 2 | import { JSONSerializable } from '../../../util/json'; 3 | import { AccAddress } from '../../bech32'; 4 | 5 | /** 6 | * A basic message for sending [[Coins]] between Terra accounts. 7 | */ 8 | export class MsgSend extends JSONSerializable { 9 | /** 10 | * value of the transaction 11 | */ 12 | public amount: Coins; 13 | 14 | /** 15 | * @param from_address sender's address 16 | * @param to_address recipient's address 17 | * @param amount value of the transaction 18 | */ 19 | constructor( 20 | public from_address: AccAddress, 21 | public to_address: AccAddress, 22 | amount: Coins.Input 23 | ) { 24 | super(); 25 | this.amount = new Coins(amount); 26 | } 27 | 28 | public static fromData(data: MsgSend.Data): MsgSend { 29 | const { 30 | value: { from_address, to_address, amount }, 31 | } = data; 32 | return new MsgSend(from_address, to_address, Coins.fromData(amount)); 33 | } 34 | 35 | public toData(): MsgSend.Data { 36 | const { from_address, to_address, amount } = this; 37 | return { 38 | type: 'bank/MsgSend', 39 | value: { 40 | from_address, 41 | to_address, 42 | amount: amount.toData(), 43 | }, 44 | }; 45 | } 46 | } 47 | 48 | export namespace MsgSend { 49 | export interface Data { 50 | type: 'bank/MsgSend'; 51 | value: { 52 | from_address: AccAddress; 53 | to_address: AccAddress; 54 | amount: Coins.Data; 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/core/gov/msgs/MsgDeposit.ts: -------------------------------------------------------------------------------- 1 | import { Coins } from '../../Coins'; 2 | import { JSONSerializable } from '../../../util/json'; 3 | import { AccAddress } from '../../bech32'; 4 | 5 | /** 6 | * Add a deposit for a proposal 7 | */ 8 | export class MsgDeposit extends JSONSerializable { 9 | public amount: Coins; 10 | /** 11 | * @param proposal_id Id of porposal to deposit to 12 | * @param depositor depositor's account address 13 | * @param amount amount to deposit 14 | */ 15 | constructor( 16 | public proposal_id: number, 17 | public depositor: AccAddress, 18 | amount: Coins.Input 19 | ) { 20 | super(); 21 | this.amount = new Coins(amount); 22 | } 23 | 24 | public static fromData(data: MsgDeposit.Data): MsgDeposit { 25 | const { 26 | value: { proposal_id, depositor, amount }, 27 | } = data; 28 | return new MsgDeposit( 29 | Number.parseInt(proposal_id), 30 | depositor, 31 | Coins.fromData(amount) 32 | ); 33 | } 34 | 35 | public toData(): MsgDeposit.Data { 36 | const { proposal_id, depositor, amount } = this; 37 | return { 38 | type: 'gov/MsgDeposit', 39 | value: { 40 | proposal_id: proposal_id.toString(), 41 | depositor, 42 | amount: amount.toData(), 43 | }, 44 | }; 45 | } 46 | } 47 | 48 | export namespace MsgDeposit { 49 | export interface Data { 50 | type: 'gov/MsgDeposit'; 51 | value: { 52 | proposal_id: string; 53 | depositor: AccAddress; 54 | amount: Coins.Data; 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/core/gov/msgs/MsgVoteWeighted.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | import { MsgVote } from './MsgVote'; 4 | 5 | /** 6 | * Weighted vote for a proposal 7 | */ 8 | export class MsgVoteWeighted extends JSONSerializable { 9 | /** 10 | * @param proposal_id ID of proposal to vote on 11 | * @param voter voter's account address 12 | * @param option one of voting options 13 | */ 14 | constructor( 15 | public proposal_id: number, 16 | public voter: AccAddress, 17 | public options: MsgVoteWeighted.Options 18 | ) { 19 | super(); 20 | } 21 | 22 | public static fromData(data: MsgVoteWeighted.Data): MsgVoteWeighted { 23 | const { 24 | value: { proposal_id, voter, options }, 25 | } = data; 26 | return new MsgVoteWeighted(Number.parseInt(proposal_id), voter, options); 27 | } 28 | 29 | public toData(): MsgVoteWeighted.Data { 30 | const { proposal_id, voter, options } = this; 31 | return { 32 | type: 'gov/MsgVoteWeighted', 33 | value: { 34 | proposal_id: proposal_id.toFixed(), 35 | voter, 36 | options, 37 | }, 38 | }; 39 | } 40 | } 41 | 42 | export namespace MsgVoteWeighted { 43 | export type Options = { 44 | option: MsgVote.Option; 45 | weight: string; 46 | }[]; 47 | 48 | export interface Data { 49 | type: 'gov/MsgVoteWeighted'; 50 | value: { 51 | proposal_id: string; 52 | voter: AccAddress; 53 | options: Options; 54 | }; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/client/lcd/api/AuthAPI.spec.ts: -------------------------------------------------------------------------------- 1 | import { APIRequester } from '../APIRequester'; 2 | import { AuthAPI } from './AuthAPI'; 3 | import { Account } from '../../../core'; 4 | import { MnemonicKey } from '../../../key'; 5 | 6 | // TODO - restore to https://lcd.terra.dev 7 | const c = new APIRequester('https://bombay-lcd.terra.dev/'); 8 | const auth = new AuthAPI(c); 9 | 10 | describe('AuthAPI', () => { 11 | describe('accounts', () => { 12 | it('account exists', async () => { 13 | const acct = await auth.accountInfo( 14 | 'terra1fa0trn2nqjc2n6mmz9txta7ky5h5nnp9m6cra3' 15 | ); 16 | 17 | expect(acct instanceof Account).toBe(true); 18 | expect(acct.coins.toData()).toContainEqual({ 19 | denom: expect.any(String), 20 | amount: expect.any(String), 21 | }); 22 | }); 23 | 24 | // TODO: - after merging CosmosSDK@v0.43.x restore vesting account test 25 | // it('LazyGradedVestingAccount', async () => { 26 | // const acct = await auth.accountInfo( 27 | // 'terra1upg95nlwkfkrq4hhjrn3k9s6ud0aqx36gwnlsn' 28 | // ); 29 | 30 | // expect(acct instanceof LazyGradedVestingAccount).toBe(true); 31 | // }); 32 | 33 | it('invalid account', async () => { 34 | await expect(auth.accountInfo('1234')).rejects.toThrow(); 35 | }); 36 | 37 | it("account doesn't exist (valid but new account)", async () => { 38 | const mk = new MnemonicKey(); 39 | const acct = await auth.accountInfo(mk.accAddress); 40 | expect((acct as Account).address).toBe(''); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/core/distribution/msgs/MsgModifyWithdrawAddress.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | 4 | /** 5 | * A validator can withdraw their outstanding commission rewards accrued from all 6 | * delegations (not including its self-delegation) into their associated account's 7 | * withdraw address. 8 | */ 9 | export class MsgModifyWithdrawAddress extends JSONSerializable { 10 | /** 11 | * @param delegator_address delegator's account address 12 | * @param withdraw_address desired new withdraw address 13 | */ 14 | constructor( 15 | public delegator_address: AccAddress, 16 | public withdraw_address: AccAddress 17 | ) { 18 | super(); 19 | } 20 | 21 | public static fromData( 22 | data: MsgModifyWithdrawAddress.Data 23 | ): MsgModifyWithdrawAddress { 24 | const { 25 | value: { delegator_address, withdraw_address }, 26 | } = data; 27 | return new MsgModifyWithdrawAddress(delegator_address, withdraw_address); 28 | } 29 | 30 | public toData(): MsgModifyWithdrawAddress.Data { 31 | const { delegator_address, withdraw_address } = this; 32 | return { 33 | type: 'distribution/MsgModifyWithdrawAddress', 34 | value: { 35 | delegator_address, 36 | withdraw_address, 37 | }, 38 | }; 39 | } 40 | } 41 | 42 | export namespace MsgModifyWithdrawAddress { 43 | export interface Data { 44 | type: 'distribution/MsgModifyWithdrawAddress'; 45 | value: { 46 | delegator_address: AccAddress; 47 | withdraw_address: AccAddress; 48 | }; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/core/market/msgs/MsgSwap.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { Coin } from '../../Coin'; 3 | import { Denom } from '../../Denom'; 4 | import { AccAddress } from '../../bech32'; 5 | 6 | /** 7 | * Executes a market swap between 2 denominations at the exchange rate registered by the 8 | * Oracle module. The account will lose the amount of coins offered, and receive funds 9 | * in the requested denomination after a swap fee has been applied. 10 | */ 11 | export class MsgSwap extends JSONSerializable { 12 | /** 13 | * @param trader trader's account address 14 | * @param offer_coin coin to be swapped (from) 15 | * @param ask_denom desired denomination (to) 16 | */ 17 | constructor( 18 | public trader: AccAddress, 19 | public offer_coin: Coin, 20 | public ask_denom: Denom 21 | ) { 22 | super(); 23 | } 24 | 25 | public static fromData(data: MsgSwap.Data): MsgSwap { 26 | const { 27 | value: { trader, offer_coin, ask_denom }, 28 | } = data; 29 | return new MsgSwap(trader, Coin.fromData(offer_coin), ask_denom); 30 | } 31 | 32 | public toData(): MsgSwap.Data { 33 | const { trader, offer_coin, ask_denom } = this; 34 | return { 35 | type: 'market/MsgSwap', 36 | value: { 37 | trader, 38 | offer_coin: offer_coin.toData(), 39 | ask_denom, 40 | }, 41 | }; 42 | } 43 | } 44 | 45 | export namespace MsgSwap { 46 | export interface Data { 47 | type: 'market/MsgSwap'; 48 | value: { 49 | trader: AccAddress; 50 | offer_coin: Coin.Data; 51 | ask_denom: Denom; 52 | }; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); 3 | // const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 4 | 5 | const commonConfig = { 6 | mode: 'production', 7 | entry: './src/index.ts', 8 | devtool: 'source-map', 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.tsx?$/, 13 | use: 'ts-loader', 14 | exclude: /node_modules/, 15 | }, 16 | ], 17 | }, 18 | resolve: { 19 | extensions: ['.tsx', '.ts', '.js'], 20 | plugins: [new TsconfigPathsPlugin()], 21 | }, 22 | plugins: [ 23 | new webpack.IgnorePlugin({ 24 | resourceRegExp: /wordlists\/(french|spanish|italian|korean|chinese_simplified|chinese_traditional|japanese)\.json$/, 25 | }), 26 | ], 27 | }; 28 | 29 | const webConfig = { 30 | ...commonConfig, 31 | target: 'web', 32 | output: { 33 | filename: 'bundle.js', 34 | libraryTarget: 'umd', 35 | library: 'Terra', 36 | }, 37 | resolve: { 38 | ...commonConfig.resolve, 39 | fallback: { 40 | stream: require.resolve('stream-browserify'), 41 | buffer: require.resolve('buffer'), 42 | }, 43 | }, 44 | plugins: [ 45 | ...commonConfig.plugins, 46 | new webpack.ProvidePlugin({ 47 | Buffer: ['buffer', 'Buffer'], 48 | }) 49 | // new BundleAnalyzerPlugin(), 50 | ], 51 | }; 52 | 53 | const nodeConfig = { 54 | ...commonConfig, 55 | target: 'node', 56 | output: { 57 | libraryTarget: 'commonjs', 58 | filename: 'bundle.node.js', 59 | }, 60 | }; 61 | 62 | module.exports = [webConfig, nodeConfig]; 63 | -------------------------------------------------------------------------------- /src/core/oracle/AggregateExchangeRateVote.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../util/json'; 2 | import { ValAddress } from '../bech32'; 3 | import { Denom } from '../Denom'; 4 | import { Coin } from '../Coin'; 5 | import { Coins } from '../Coins'; 6 | 7 | /** 8 | * Stores information about data about Oracle aggregate vote fetched from the blockchain. 9 | */ 10 | export class AggregateExchangeRateVote extends JSONSerializable { 11 | /** 12 | * @param exchange_rate_tuples exchange rates for LUNA 13 | * @param voter validator 14 | */ 15 | constructor(public exchange_rate_tuples: Coins, public voter: ValAddress) { 16 | super(); 17 | } 18 | 19 | public static fromData( 20 | data: AggregateExchangeRateVote.Data 21 | ): AggregateExchangeRateVote { 22 | const { exchange_rate_tuples, voter } = data; 23 | const xr_coins = new Coins( 24 | exchange_rate_tuples.map(t => new Coin(t.denom, t.exchange_rate)) 25 | ).toDecCoins(); 26 | return new AggregateExchangeRateVote(xr_coins, voter); 27 | } 28 | 29 | public toData(): AggregateExchangeRateVote.Data { 30 | const { exchange_rate_tuples, voter } = this; 31 | return { 32 | exchange_rate_tuples: exchange_rate_tuples.map(c => ({ 33 | denom: c.denom, 34 | exchange_rate: c.amount.toString(), 35 | })), 36 | voter, 37 | }; 38 | } 39 | } 40 | 41 | export interface ExchangeRateTuple { 42 | denom: Denom; 43 | exchange_rate: string; 44 | } 45 | 46 | export namespace AggregateExchangeRateVote { 47 | export interface Data { 48 | exchange_rate_tuples: ExchangeRateTuple[]; 49 | voter: ValAddress; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/core/distribution/params.ts: -------------------------------------------------------------------------------- 1 | import { ParamChange } from '../params/ParamChange'; 2 | import { Convert } from '../../util/convert'; 3 | import { Dec } from '../numeric'; 4 | 5 | type CommunityTax = ParamChange.Type<'distribution', 'communitytax', Dec>; 6 | 7 | type BaseProposerReward = ParamChange.Type< 8 | 'distribution', 9 | 'baseproposerreward', 10 | Dec 11 | >; 12 | type BonusProposerReward = ParamChange.Type< 13 | 'distribution', 14 | 'bonusproposerreward', 15 | Dec 16 | >; 17 | 18 | type WithdrawAddrEnabled = ParamChange.Type< 19 | 'distribution', 20 | 'withdrawaddrenabled', 21 | boolean 22 | >; 23 | 24 | export type DistributionParamChange = 25 | | CommunityTax 26 | | BaseProposerReward 27 | | BonusProposerReward 28 | | WithdrawAddrEnabled; 29 | 30 | export namespace DistributionParamChange { 31 | export type Data = 32 | | ParamChange.Data.Type 33 | | ParamChange.Data.Type 34 | | ParamChange.Data.Type 35 | | ParamChange.Data.Type; 36 | } 37 | 38 | export type DistributionParamChanges = { 39 | distribution?: { 40 | communitytax?: Dec; 41 | baseproposerreward?: Dec; 42 | bonusproposerreward?: Dec; 43 | withdrawaddrenabled?: boolean; 44 | }; 45 | }; 46 | 47 | export namespace DistributionParamChanges { 48 | export const ConversionTable = { 49 | distribution: { 50 | communitytax: [Convert.toDec, Convert.toString], 51 | baseproposerreward: [Convert.toDec, Convert.toString], 52 | bonusproposerreward: [Convert.toDec, Convert.toString], 53 | withdrawaddrenabled: [Convert.id, Convert.id], 54 | }, 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /src/client/lcd/api/TendermintAPI.ts: -------------------------------------------------------------------------------- 1 | import { BaseAPI } from './BaseAPI'; 2 | import { BlockInfo, ValidatorSet } from '../../../core'; 3 | import { APIParams } from '../APIRequester'; 4 | 5 | export class TendermintAPI extends BaseAPI { 6 | /** 7 | * Gets the node's information. 8 | */ 9 | public async nodeInfo(params: APIParams = {}): Promise { 10 | return this.c.getRaw(`/node_info`, params); 11 | } 12 | 13 | /** 14 | * Gets whether the node is currently in syncing mode to catch up with blocks. 15 | */ 16 | public async syncing(params: APIParams = {}): Promise { 17 | return this.c 18 | .getRaw<{ syncing: boolean }>(`/syncing`, params) 19 | .then(d => d.syncing); 20 | } 21 | 22 | /** 23 | * Gets the validator (delegates) set at the specific height. If no height is given, the current set is returned. 24 | * @param height block height 25 | */ 26 | public async validatorSet( 27 | height?: number, 28 | params: APIParams = {} 29 | ): Promise { 30 | const url = 31 | height !== undefined 32 | ? `/validatorsets/${height}` 33 | : `/validatorsets/latest`; 34 | return this.c.get(url, params).then(d => d.result); 35 | } 36 | 37 | /** 38 | * Gets the block information at the specified height. If no height is given, the latest block is returned. 39 | * @param height block height. 40 | */ 41 | public async blockInfo( 42 | height?: number, 43 | params: APIParams = {} 44 | ): Promise { 45 | const url = height !== undefined ? `/blocks/${height}` : `/blocks/latest`; 46 | return this.c.getRaw(url, params); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/core/oracle/msgs/MsgDelegateFeedConsent.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress, ValAddress } from '../../bech32'; 3 | 4 | /** 5 | * A **feeeder** is an account which is responsible for signing transactions with Oracle vote 6 | * and prevote messages on behalf of the validator. The blockchain will reject 7 | * [[MsgExchangeRateVote]] and [[MsgExchangeRatePrevote]] messages in transactions 8 | * signed by an 9 | * account different than the registered feeder. 10 | * 11 | * The following message registers a validator's feeder address. 12 | */ 13 | export class MsgDelegateFeedConsent extends JSONSerializable { 14 | /** 15 | * @param operator validator's operator address 16 | * @param delegate account address to set to feeder 17 | */ 18 | constructor(public operator: ValAddress, public delegate: AccAddress) { 19 | super(); 20 | } 21 | 22 | public static fromData( 23 | data: MsgDelegateFeedConsent.Data 24 | ): MsgDelegateFeedConsent { 25 | const { 26 | value: { operator, delegate }, 27 | } = data; 28 | return new MsgDelegateFeedConsent(operator, delegate); 29 | } 30 | 31 | public toData(): MsgDelegateFeedConsent.Data { 32 | const { operator, delegate } = this; 33 | return { 34 | type: 'oracle/MsgDelegateFeedConsent', 35 | value: { 36 | operator, 37 | delegate, 38 | }, 39 | }; 40 | } 41 | } 42 | 43 | export namespace MsgDelegateFeedConsent { 44 | export interface Data { 45 | type: 'oracle/MsgDelegateFeedConsent'; 46 | value: { 47 | operator: ValAddress; 48 | delegate: AccAddress; 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/core/staking/msgs/MsgDelegate.ts: -------------------------------------------------------------------------------- 1 | import { Coin } from '../../Coin'; 2 | import { JSONSerializable } from '../../../util/json'; 3 | import { AccAddress, ValAddress } from '../../bech32'; 4 | 5 | /** 6 | * A delegator can submit this message to send more Luna to be staked through a 7 | * validator delegate. 8 | */ 9 | export class MsgDelegate extends JSONSerializable { 10 | /** 11 | * 12 | * @param delegator_address delegator's account address 13 | * @param validator_address validator's operator address 14 | * @param amount amount of LUNA to be sent for delegation 15 | */ 16 | constructor( 17 | public delegator_address: AccAddress, 18 | public validator_address: ValAddress, 19 | public amount: Coin 20 | ) { 21 | super(); 22 | } 23 | 24 | public static fromData(data: MsgDelegate.Data): MsgDelegate { 25 | const { 26 | value: { delegator_address, validator_address, amount }, 27 | } = data; 28 | return new MsgDelegate( 29 | delegator_address, 30 | validator_address, 31 | Coin.fromData(amount) 32 | ); 33 | } 34 | 35 | public toData(): MsgDelegate.Data { 36 | const { delegator_address, validator_address, amount } = this; 37 | return { 38 | type: 'staking/MsgDelegate', 39 | value: { 40 | delegator_address, 41 | validator_address, 42 | amount: amount.toData(), 43 | }, 44 | }; 45 | } 46 | } 47 | 48 | export namespace MsgDelegate { 49 | export interface Data { 50 | type: 'staking/MsgDelegate'; 51 | value: { 52 | delegator_address: AccAddress; 53 | validator_address: ValAddress; 54 | amount: Coin.Data; 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/core/wasm/msgs/MsgMigrateContract.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable, removeNull } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | export class MsgMigrateContract extends JSONSerializable { 4 | /** 5 | * @param admin contract admin 6 | * @param contract contract address to be migrated from 7 | * @param new_code_id reference to the new code on the blockchain 8 | * @param migrate_msg JSON message to configure the migrate state of the contract 9 | */ 10 | constructor( 11 | public admin: AccAddress, 12 | public contract: AccAddress, 13 | public new_code_id: number, 14 | public migrate_msg: object // json object 15 | ) { 16 | super(); 17 | } 18 | 19 | public static fromData(data: MsgMigrateContract.Data): MsgMigrateContract { 20 | const { 21 | value: { admin, contract, new_code_id, migrate_msg }, 22 | } = data; 23 | return new MsgMigrateContract( 24 | admin, 25 | contract, 26 | Number.parseInt(new_code_id), 27 | migrate_msg 28 | ); 29 | } 30 | 31 | public toData(): MsgMigrateContract.Data { 32 | const { admin, contract, new_code_id, migrate_msg } = this; 33 | return { 34 | type: 'wasm/MsgMigrateContract', 35 | value: { 36 | admin, 37 | contract, 38 | new_code_id: new_code_id.toFixed(), 39 | migrate_msg: removeNull(migrate_msg), 40 | }, 41 | }; 42 | } 43 | } 44 | 45 | export namespace MsgMigrateContract { 46 | export interface Data { 47 | type: 'wasm/MsgMigrateContract'; 48 | value: { 49 | admin: AccAddress; 50 | contract: AccAddress; 51 | new_code_id: string; 52 | migrate_msg: object; 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/core/auth/LazyGradedVestingAccount.data.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "type": "core/LazyGradedVestingAccount", 3 | "value": { 4 | "address": "terra1upg95nlwkfkrq4hhjrn3k9s6ud0aqx36gwnlsn", 5 | "coins": [{ 6 | "denom": "ukrw", 7 | "amount": "3952727625434" 8 | }, 9 | { 10 | "denom": "uluna", 11 | "amount": "48919046" 12 | }, 13 | { 14 | "denom": "umnt", 15 | "amount": "35243811596" 16 | }, 17 | { 18 | "denom": "usdr", 19 | "amount": "1212381" 20 | }, 21 | { 22 | "denom": "uusd", 23 | "amount": "474532" 24 | } 25 | ], 26 | "public_key": null, 27 | "account_number": "684082", 28 | "sequence": "0", 29 | "original_vesting": [{ 30 | "denom": "uluna", 31 | "amount": "5000000000000" 32 | }], 33 | "delegated_free": [], 34 | "delegated_vesting": [{ 35 | "denom": "uluna", 36 | "amount": "1338029091449" 37 | }], 38 | "end_time": "0", 39 | "vesting_schedules": [{ 40 | "denom": "uluna", 41 | "schedules": [{ 42 | "start_time": "1558677600", 43 | "end_time": "1561356000", 44 | "ratio": "0.100000000000000000" 45 | }, 46 | { 47 | "start_time": "1561356000", 48 | "end_time": "1587708000", 49 | "ratio": "0.270000000000000000" 50 | }, 51 | { 52 | "start_time": "1587708000", 53 | "end_time": "1600927200", 54 | "ratio": "0.480000000000000000" 55 | }, 56 | { 57 | "start_time": "1600927200", 58 | "end_time": "1603519200", 59 | "ratio": "0.150000000000000000" 60 | } 61 | ] 62 | }] 63 | } 64 | }] -------------------------------------------------------------------------------- /src/core/distribution/msgs/MsgWithdrawDelegationReward.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress, ValAddress } from '../../bech32'; 3 | 4 | /** 5 | * A delegator can withdraw currently outstanding rewards accrued from their delegation 6 | * toward a validator by submitting the following message. 7 | * 8 | * The rewards will be deposited to their Withdraw Address. 9 | */ 10 | export class MsgWithdrawDelegationReward extends JSONSerializable { 11 | /** 12 | * 13 | * @param delegator_address delegator's account address 14 | * @param validator_address validator's operator address 15 | */ 16 | constructor( 17 | public delegator_address: AccAddress, 18 | public validator_address: ValAddress 19 | ) { 20 | super(); 21 | } 22 | 23 | public static fromData( 24 | data: MsgWithdrawDelegationReward.Data 25 | ): MsgWithdrawDelegationReward { 26 | const { 27 | value: { delegator_address, validator_address }, 28 | } = data; 29 | return new MsgWithdrawDelegationReward( 30 | delegator_address, 31 | validator_address 32 | ); 33 | } 34 | 35 | public toData(): MsgWithdrawDelegationReward.Data { 36 | const { delegator_address, validator_address } = this; 37 | return { 38 | type: 'distribution/MsgWithdrawDelegationReward', 39 | value: { 40 | delegator_address, 41 | validator_address, 42 | }, 43 | }; 44 | } 45 | } 46 | 47 | export namespace MsgWithdrawDelegationReward { 48 | export interface Data { 49 | type: 'distribution/MsgWithdrawDelegationReward'; 50 | value: { 51 | delegator_address: AccAddress; 52 | validator_address: ValAddress; 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/core/wasm/msgs/MsgExecuteContract.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable, removeNull } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | import { Coins } from '../../Coins'; 4 | export class MsgExecuteContract extends JSONSerializable { 5 | public coins: Coins; 6 | 7 | /** 8 | * @param sender contract user 9 | * @param contract contract address 10 | * @param msg HandleMsg to pass as arguments for contract invocation 11 | * @param coins coins to be sent to contract 12 | */ 13 | constructor( 14 | public sender: AccAddress, 15 | public contract: AccAddress, 16 | public execute_msg: object, 17 | coins: Coins.Input = {} 18 | ) { 19 | super(); 20 | this.coins = new Coins(coins); 21 | } 22 | 23 | public static fromData(data: MsgExecuteContract.Data): MsgExecuteContract { 24 | const { 25 | value: { sender, contract, execute_msg, coins }, 26 | } = data; 27 | return new MsgExecuteContract( 28 | sender, 29 | contract, 30 | execute_msg, 31 | Coins.fromData(coins) 32 | ); 33 | } 34 | 35 | public toData(): MsgExecuteContract.Data { 36 | const { sender, contract, execute_msg, coins } = this; 37 | 38 | return { 39 | type: 'wasm/MsgExecuteContract', 40 | value: { 41 | sender, 42 | contract, 43 | execute_msg: removeNull(execute_msg), 44 | coins: coins.toData(), 45 | }, 46 | }; 47 | } 48 | } 49 | 50 | export namespace MsgExecuteContract { 51 | export interface Data { 52 | type: 'wasm/MsgExecuteContract'; 53 | value: { 54 | sender: AccAddress; 55 | contract: AccAddress; 56 | execute_msg: object; 57 | coins: Coins.Data; 58 | }; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Block'; 2 | export * from './Coin'; 3 | export * from './Coins'; 4 | export * from './Denom'; 5 | export * from './Msg'; 6 | export * from './numeric'; 7 | export * from './PublicKey'; 8 | export * from './StdFee'; 9 | export * from './StdSignature'; 10 | export * from './StdSignMsg'; 11 | export * from './StdTx'; 12 | export * from './TxInfo'; 13 | export * from './ValidatorSet'; 14 | export * from './Deposit'; 15 | 16 | // Auth 17 | export * from './auth/Account'; 18 | export * from './auth/LazyGradedVestingAccount'; 19 | 20 | // Bank 21 | export * from './bank/msgs'; 22 | 23 | // Distribution 24 | export * from './distribution/msgs'; 25 | export * from './distribution/proposals'; 26 | 27 | // Governance 28 | export * from './gov/msgs'; 29 | export * from './gov/proposals'; 30 | export * from './gov/Proposal'; 31 | export * from './gov/Vote'; 32 | 33 | // Market 34 | export * from './market/msgs'; 35 | 36 | // MsgAuth 37 | export * from './msgauth/msgs'; 38 | export * from './msgauth/Authorization'; 39 | 40 | // Oracle 41 | export * from './oracle/msgs'; 42 | export * from './oracle/AggregateExchangeRatePrevote'; 43 | export * from './oracle/AggregateExchangeRateVote'; 44 | 45 | // Parameters 46 | export * from './params/proposals'; 47 | export * from './params/ParamChange'; 48 | 49 | // Slashing 50 | export * from './slashing/msgs'; 51 | 52 | // Staking 53 | export * from './staking/msgs'; 54 | export * from './staking/Delegation'; 55 | export * from './staking/Redelegation'; 56 | export * from './staking/UnbondingDelegation'; 57 | export * from './staking/Validator'; 58 | 59 | // Treasury 60 | export * from './treasury/PolicyConstraints'; 61 | 62 | // WASM 63 | export * from './wasm/msgs'; 64 | 65 | // bech32 types 66 | export * from './bech32'; 67 | -------------------------------------------------------------------------------- /src/core/gov/msgs/MsgSubmitProposal.ts: -------------------------------------------------------------------------------- 1 | import { Coins } from '../../Coins'; 2 | import { Proposal } from '../Proposal'; 3 | import { JSONSerializable } from '../../../util/json'; 4 | import { AccAddress } from '../../bech32'; 5 | 6 | /** 7 | * Submit a proposal alongside an initial deposit. 8 | */ 9 | export class MsgSubmitProposal extends JSONSerializable { 10 | public initial_deposit: Coins; 11 | 12 | /** 13 | * @param content proposal content to submit 14 | * @param initial_deposit deposit provided 15 | * @param proposer proposer's account address 16 | */ 17 | constructor( 18 | public content: Proposal.Content, 19 | initial_deposit: Coins.Input, 20 | public proposer: AccAddress 21 | ) { 22 | super(); 23 | this.initial_deposit = new Coins(initial_deposit); 24 | } 25 | 26 | public static fromData(data: MsgSubmitProposal.Data): MsgSubmitProposal { 27 | const { 28 | value: { content, initial_deposit, proposer }, 29 | } = data; 30 | return new MsgSubmitProposal( 31 | Proposal.Content.fromData(content), 32 | Coins.fromData(initial_deposit), 33 | proposer 34 | ); 35 | } 36 | 37 | public toData(): MsgSubmitProposal.Data { 38 | const { content, initial_deposit, proposer } = this; 39 | return { 40 | type: 'gov/MsgSubmitProposal', 41 | value: { 42 | content: content.toData(), 43 | initial_deposit: initial_deposit.toData(), 44 | proposer, 45 | }, 46 | }; 47 | } 48 | } 49 | 50 | export namespace MsgSubmitProposal { 51 | export interface Data { 52 | type: 'gov/MsgSubmitProposal'; 53 | value: { 54 | content: Proposal.Content.Data; 55 | initial_deposit: Coins.Data; 56 | proposer: AccAddress; 57 | }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/core/staking/msgs/MsgUndelegate.ts: -------------------------------------------------------------------------------- 1 | import { Coin } from '../../Coin'; 2 | import { JSONSerializable } from '../../../util/json'; 3 | import { AccAddress, ValAddress } from '../../bech32'; 4 | 5 | /** 6 | * A delegator can undelegate an amount of bonded Luna, and will begin the unbonding 7 | * process for those funds. The unbonding process takes 21 days to complete, during 8 | * which the Luna cannot be transacted or swapped. 9 | */ 10 | export class MsgUndelegate extends JSONSerializable { 11 | /** 12 | * @param delegator_address delegator's account address 13 | * @param validator_address validator's operator address 14 | * @param amount Luna to be undelegated 15 | */ 16 | constructor( 17 | public delegator_address: AccAddress, 18 | public validator_address: ValAddress, 19 | public amount: Coin 20 | ) { 21 | super(); 22 | } 23 | 24 | public static fromData(data: MsgUndelegate.Data): MsgUndelegate { 25 | const { 26 | value: { delegator_address, validator_address, amount }, 27 | } = data; 28 | return new MsgUndelegate( 29 | delegator_address, 30 | validator_address, 31 | Coin.fromData(amount) 32 | ); 33 | } 34 | 35 | public toData(): MsgUndelegate.Data { 36 | const { delegator_address, validator_address, amount } = this; 37 | return { 38 | type: 'staking/MsgUndelegate', 39 | value: { 40 | delegator_address, 41 | validator_address, 42 | amount: amount.toData(), 43 | }, 44 | }; 45 | } 46 | } 47 | 48 | export namespace MsgUndelegate { 49 | export interface Data { 50 | type: 'staking/MsgUndelegate'; 51 | value: { 52 | delegator_address: AccAddress; 53 | validator_address: ValAddress; 54 | amount: Coin.Data; 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/core/staking/Delegation.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../util/json'; 2 | import { Dec } from '../numeric'; 3 | import { AccAddress, ValAddress } from '../bech32'; 4 | import { Coin } from '../Coin'; 5 | 6 | /** 7 | * Stores information about the status of a delegation between a delegator and validator, fetched from the blockchain. 8 | */ 9 | export class Delegation extends JSONSerializable { 10 | /** 11 | * @param delegator_address delegator's account address 12 | * @param validator_address validator's operator address 13 | * @param shares delegator's shares 14 | * @param balance balance of the delegation 15 | */ 16 | constructor( 17 | public delegator_address: AccAddress, 18 | public validator_address: ValAddress, 19 | public shares: Dec, 20 | public balance: Coin 21 | ) { 22 | super(); 23 | } 24 | 25 | public static fromData(data: Delegation.Data): Delegation { 26 | const { 27 | delegation: { delegator_address, validator_address, shares }, 28 | balance, 29 | } = data; 30 | return new Delegation( 31 | delegator_address, 32 | validator_address, 33 | new Dec(shares), 34 | Coin.fromData(balance) 35 | ); 36 | } 37 | 38 | public toData(): Delegation.Data { 39 | const { delegator_address, validator_address, shares, balance } = this; 40 | 41 | return { 42 | delegation: { 43 | delegator_address, 44 | validator_address, 45 | shares: shares.toString(), 46 | }, 47 | balance: balance.toData(), 48 | }; 49 | } 50 | } 51 | 52 | export namespace Delegation { 53 | export interface Data { 54 | delegation: { 55 | delegator_address: AccAddress; 56 | validator_address: ValAddress; 57 | shares: string; 58 | }; 59 | balance: Coin.Data; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/core/mint/params.ts: -------------------------------------------------------------------------------- 1 | import { Denom } from '../Denom'; 2 | import { ParamChange } from '../params/ParamChange'; 3 | import { Convert } from '../../util/convert'; 4 | import { Dec } from '../numeric'; 5 | 6 | type MintDenom = ParamChange.Type<'mint', 'MintDenom', Denom>; 7 | type InflationRateChange = ParamChange.Type<'mint', 'InflationRateChange', Dec>; 8 | type InflationMax = ParamChange.Type<'mint', 'InflationMax', Dec>; 9 | type InflationMin = ParamChange.Type<'mint', 'InflationMin', Dec>; 10 | type GoalBonded = ParamChange.Type<'mint', 'GoalBonded', Dec>; 11 | type BlocksPerYear = ParamChange.Type<'mint', 'BlocksPerYear', number>; 12 | 13 | export type MintParamChange = 14 | | MintDenom 15 | | InflationRateChange 16 | | InflationMax 17 | | InflationMin 18 | | GoalBonded 19 | | BlocksPerYear; 20 | 21 | export namespace MintParamChange { 22 | export type Data = 23 | | ParamChange.Data.Type 24 | | ParamChange.Data.Type 25 | | ParamChange.Data.Type 26 | | ParamChange.Data.Type 27 | | ParamChange.Data.Type 28 | | ParamChange.Data.Type; 29 | } 30 | 31 | export interface MintParamChanges { 32 | mint?: { 33 | MintDenom?: Denom; 34 | InflationRateChange?: Dec; 35 | InflationMax?: Dec; 36 | InflationMin?: Dec; 37 | GoalBonded?: Dec; 38 | BlocksPerYear?: number; 39 | }; 40 | } 41 | 42 | export namespace MintParamChanges { 43 | export const ConversionTable = { 44 | mint: { 45 | MintDenom: [Convert.id, Convert.id], 46 | InflationRateChange: [Convert.toDec, Convert.toString], 47 | InflationMax: [Convert.toDec, Convert.toString], 48 | InflationMin: [Convert.toDec, Convert.toString], 49 | GoalBonded: [Convert.toDec, Convert.toString], 50 | BlocksPerYear: [Convert.toNumber, Convert.toFixed], 51 | }, 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /src/core/msgauth/msgs/MsgGrantAuthorization.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | import { Authorization } from '../Authorization'; 4 | 5 | export class MsgGrantAuthorization extends JSONSerializable { 6 | /** 7 | * @param depositor depositor's account address 8 | * @param amount coins to fund the community pool 9 | */ 10 | constructor( 11 | public granter: AccAddress, 12 | public grantee: AccAddress, 13 | public authorization: Authorization, 14 | public expiration: Date 15 | ) { 16 | super(); 17 | } 18 | 19 | public static fromData( 20 | data: MsgGrantAuthorization.Data 21 | ): MsgGrantAuthorization { 22 | const { 23 | value: { 24 | granter, 25 | grantee, 26 | grant: { authorization, expiration }, 27 | }, 28 | } = data; 29 | return new MsgGrantAuthorization( 30 | granter, 31 | grantee, 32 | Authorization.fromData(authorization), 33 | new Date(expiration) 34 | ); 35 | } 36 | 37 | public toData(): MsgGrantAuthorization.Data { 38 | const { granter, grantee, authorization, expiration } = this; 39 | return { 40 | type: 'msgauth/MsgGrantAuthorization', 41 | value: { 42 | granter, 43 | grantee, 44 | grant: { 45 | authorization: authorization.toData(), 46 | expiration: expiration.toISOString().replace(/\.000Z$/, 'Z'), 47 | }, 48 | }, 49 | }; 50 | } 51 | } 52 | 53 | export namespace MsgGrantAuthorization { 54 | export interface Data { 55 | type: 'msgauth/MsgGrantAuthorization'; 56 | value: { 57 | granter: AccAddress; 58 | grantee: AccAddress; 59 | grant: { 60 | authorization: Authorization.Data; 61 | expiration: string; 62 | }; 63 | }; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/client/lcd/api/MintAPI.ts: -------------------------------------------------------------------------------- 1 | import { Dec, Numeric, Denom } from '../../../core'; 2 | import { APIParams } from '../APIRequester'; 3 | import { BaseAPI } from './BaseAPI'; 4 | 5 | export interface MintingParams { 6 | mint_denom: Denom; 7 | inflation_rate_change: Dec; 8 | inflation_max: Dec; 9 | inflation_min: Dec; 10 | goal_bonded: Dec; 11 | blocks_per_year: number; 12 | } 13 | 14 | export namespace MintingParams { 15 | export interface Data { 16 | mint_denom: string; 17 | inflation_rate_change: string; 18 | inflation_max: string; 19 | inflation_min: string; 20 | goal_bonded: string; 21 | blocks_per_year: string; 22 | } 23 | } 24 | 25 | export class MintAPI extends BaseAPI { 26 | /** 27 | * Gets the current minting inflation value 28 | */ 29 | public async inflation(params: APIParams = {}): Promise { 30 | return this.c 31 | .get(`/minting/inflation`, params) 32 | .then(d => new Dec(d.result)); 33 | } 34 | 35 | /** 36 | * Gets the current minting annaul provisions value 37 | */ 38 | public async annualProvisions(params: APIParams = {}): Promise { 39 | return this.c 40 | .get(`minting/annual-provisions`, params) 41 | .then(d => new Dec(d.result)); 42 | } 43 | 44 | /** 45 | * Gets the current minting module's parameters. 46 | */ 47 | public async parameters(params: APIParams = {}): Promise { 48 | return this.c 49 | .get(`/minting/parameters`, params) 50 | .then(({ result: d }) => ({ 51 | mint_denom: d.mint_denom, 52 | inflation_rate_change: new Dec(d.inflation_rate_change), 53 | inflation_max: new Dec(d.inflation_max), 54 | inflation_min: new Dec(d.inflation_min), 55 | goal_bonded: new Dec(d.goal_bonded), 56 | blocks_per_year: Number.parseInt(d.blocks_per_year), 57 | })); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/core/distribution/proposals/CommunityPoolSpendProposal.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { Coins } from '../../Coins'; 3 | import { AccAddress } from '../../bech32'; 4 | 5 | /** 6 | * Proposal that disburses funds from the Distribution module's community pool to the 7 | * specified recipient if passed. 8 | */ 9 | export class CommunityPoolSpendProposal extends JSONSerializable { 10 | public amount: Coins; 11 | /** 12 | * @param title proposal's title 13 | * @param description proposal's description 14 | * @param recipient recipient address 15 | * @param amount amount to give recipient 16 | */ 17 | constructor( 18 | public title: string, 19 | public description: string, 20 | public recipient: AccAddress, 21 | amount: Coins.Input 22 | ) { 23 | super(); 24 | this.amount = new Coins(amount); 25 | } 26 | 27 | public static fromData( 28 | data: CommunityPoolSpendProposal.Data 29 | ): CommunityPoolSpendProposal { 30 | const { 31 | value: { title, description, recipient, amount }, 32 | } = data; 33 | return new CommunityPoolSpendProposal( 34 | title, 35 | description, 36 | recipient, 37 | Coins.fromData(amount) 38 | ); 39 | } 40 | 41 | public toData(): CommunityPoolSpendProposal.Data { 42 | const { title, description, recipient, amount } = this; 43 | return { 44 | type: 'distribution/CommunityPoolSpendProposal', 45 | value: { 46 | title, 47 | description, 48 | recipient, 49 | amount: amount.toData(), 50 | }, 51 | }; 52 | } 53 | } 54 | 55 | export namespace CommunityPoolSpendProposal { 56 | export interface Data { 57 | type: 'distribution/CommunityPoolSpendProposal'; 58 | value: { 59 | title: string; 60 | description: string; 61 | recipient: AccAddress; 62 | amount: Coins.Data; 63 | }; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/core/oracle/AggregateExchangeRateVote.data.json: -------------------------------------------------------------------------------- 1 | { 2 | "exchange_rate_tuples": [ 3 | { 4 | "denom": "uaud", 5 | "exchange_rate": "8.099497011452464082" 6 | }, 7 | { 8 | "denom": "ucad", 9 | "exchange_rate": "7.577173472450800448" 10 | }, 11 | { 12 | "denom": "uchf", 13 | "exchange_rate": "5.606272651951189670" 14 | }, 15 | { 16 | "denom": "ucny", 17 | "exchange_rate": "39.452837979258993140" 18 | }, 19 | { 20 | "denom": "udkk", 21 | "exchange_rate": "38.241047368775133511" 22 | }, 23 | { 24 | "denom": "ueur", 25 | "exchange_rate": "5.139663623776370157" 26 | }, 27 | { 28 | "denom": "ugbp", 29 | "exchange_rate": "4.408410669174041069" 30 | }, 31 | { 32 | "denom": "uhkd", 33 | "exchange_rate": "47.879657741819166432" 34 | }, 35 | { 36 | "denom": "uinr", 37 | "exchange_rate": "455.278089535410089886" 38 | }, 39 | { 40 | "denom": "ujpy", 41 | "exchange_rate": "682.913652146188456601" 42 | }, 43 | { 44 | "denom": "ukrw", 45 | "exchange_rate": "7212.006856855468600762" 46 | }, 47 | { 48 | "denom": "umnt", 49 | "exchange_rate": "17546.449467252153327358" 50 | }, 51 | { 52 | "denom": "unok", 53 | "exchange_rate": "52.260211155579785444" 54 | }, 55 | { 56 | "denom": "usdr", 57 | "exchange_rate": "4.283053019813641797" 58 | }, 59 | { 60 | "denom": "usek", 61 | "exchange_rate": "52.204496644752941323" 62 | }, 63 | { 64 | "denom": "usgd", 65 | "exchange_rate": "8.238783288519574384" 66 | }, 67 | { 68 | "denom": "uthb", 69 | "exchange_rate": "193.148280408961856289" 70 | }, 71 | { 72 | "denom": "uusd", 73 | "exchange_rate": "6.170382074072986394" 74 | } 75 | ], 76 | "voter": "terravaloper1krj7amhhagjnyg2tkkuh6l0550y733jnjnnlzy" 77 | } 78 | -------------------------------------------------------------------------------- /src/client/lcd/LCDUtils.ts: -------------------------------------------------------------------------------- 1 | import { LCDClient } from './LCDClient'; 2 | import { Coin } from '../../core/Coin'; 3 | import { Int, Dec } from '../../core/numeric'; 4 | import { Validator } from '../../core/staking/Validator'; 5 | 6 | interface ValidatorWithVotingPower { 7 | validatorInfo: Validator; 8 | votingPower: number; 9 | proposerPriority: number; 10 | } 11 | 12 | export class LCDUtils { 13 | constructor(public lcd: LCDClient) {} 14 | 15 | /** 16 | * Calculates the tax that would be applied for the Coin if sent. 17 | * Tax = min(taxCap, taxRate * amount) 18 | * @param coin 19 | */ 20 | public async calculateTax(coin: Coin): Promise { 21 | const rate = await this.lcd.treasury.taxRate(); 22 | const cap = await this.lcd.treasury.taxCap(coin.denom); 23 | return new Coin( 24 | coin.denom, 25 | Int.ceil(Dec.min(coin.amount.mul(rate), cap.amount)) 26 | ); 27 | } 28 | 29 | /** 30 | * Gets current validators and merges their voting power from the validator set query. 31 | */ 32 | public async validatorsWithVotingPower(): Promise<{ 33 | [validatorAddress: string]: ValidatorWithVotingPower; 34 | }> { 35 | const validatorSetResponse = await this.lcd.tendermint.validatorSet(); 36 | const validators = await this.lcd.staking.validators(); 37 | const validatorSet = validatorSetResponse.validators.reduce((m: any, o) => { 38 | m[o.pub_key.value] = o; 39 | return m; 40 | }, {}); 41 | 42 | const res: { [k: string]: ValidatorWithVotingPower } = {}; 43 | 44 | for (const v of validators) { 45 | const delegateInfo = validatorSet[v.consensus_pubkey.value]; 46 | if (delegateInfo === undefined) continue; 47 | res[v.operator_address] = { 48 | validatorInfo: v, 49 | votingPower: Number.parseInt(delegateInfo.voting_power), 50 | proposerPriority: Number.parseInt(delegateInfo.proposer_priority), 51 | }; 52 | } 53 | 54 | return res; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/core/market/msgs/MsgSwapSend.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { Coin } from '../../Coin'; 3 | import { Denom } from '../../Denom'; 4 | import { AccAddress } from '../../bech32'; 5 | 6 | /** 7 | * Executes a market swap send between 2 denominations at the exchange rate registered by the 8 | * Oracle module. The sender account will lose the amount of coins offered, and receiver will receive funds 9 | * in the requested denomination after a swap and send fee has been applied. 10 | */ 11 | export class MsgSwapSend extends JSONSerializable { 12 | /** 13 | * @param from_address sender's account address 14 | * @param to_address receiver's account address 15 | * @param offer_coin coin to be swapped (from) 16 | * @param ask_denom desired denomination (to) 17 | */ 18 | constructor( 19 | public from_address: AccAddress, 20 | public to_address: AccAddress, 21 | public offer_coin: Coin, 22 | public ask_denom: Denom 23 | ) { 24 | super(); 25 | } 26 | 27 | public static fromData(data: MsgSwapSend.Data): MsgSwapSend { 28 | const { 29 | value: { from_address, to_address, offer_coin, ask_denom }, 30 | } = data; 31 | return new MsgSwapSend( 32 | from_address, 33 | to_address, 34 | Coin.fromData(offer_coin), 35 | ask_denom 36 | ); 37 | } 38 | 39 | public toData(): MsgSwapSend.Data { 40 | const { from_address, to_address, offer_coin, ask_denom } = this; 41 | return { 42 | type: 'market/MsgSwapSend', 43 | value: { 44 | from_address, 45 | to_address, 46 | offer_coin: offer_coin.toData(), 47 | ask_denom, 48 | }, 49 | }; 50 | } 51 | } 52 | 53 | export namespace MsgSwapSend { 54 | export interface Data { 55 | type: 'market/MsgSwapSend'; 56 | value: { 57 | from_address: AccAddress; 58 | to_address: AccAddress; 59 | offer_coin: Coin.Data; 60 | ask_denom: Denom; 61 | }; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/core/StdTx.ts: -------------------------------------------------------------------------------- 1 | import { StdSignature } from './StdSignature'; 2 | import { JSONSerializable } from '../util/json'; 3 | import { StdFee } from './StdFee'; 4 | import { Msg } from './Msg'; 5 | 6 | /** 7 | * The StdTx data structure contains the signatures from [[StdSignMsg]] with the same 8 | * information, and can be broadcasted to the node to be included in a block. 9 | */ 10 | export class StdTx extends JSONSerializable { 11 | /** 12 | * @param msg list of messages to include (not a typo) 13 | * @param fee transaction fee 14 | * @param signatures list of signatures 15 | * @param memo optional note 16 | * @param timeout_height optional tx timeout 17 | */ 18 | constructor( 19 | public msg: Msg[], 20 | public fee: StdFee, 21 | public signatures: StdSignature[], 22 | public memo: string = '', 23 | public timeout_height: number = 0 24 | ) { 25 | super(); 26 | } 27 | 28 | public static fromData(data: StdTx.Data): StdTx { 29 | const { 30 | value: { msg, fee, signatures, memo, timeout_height }, 31 | } = data; 32 | return new StdTx( 33 | msg.map(m => Msg.fromData(m)), 34 | StdFee.fromData(fee), 35 | signatures.map(s => StdSignature.fromData(s)), 36 | memo, 37 | Number.parseInt(timeout_height) 38 | ); 39 | } 40 | 41 | public toData(): StdTx.Data { 42 | const { msg, fee, signatures, memo, timeout_height } = this; 43 | return { 44 | type: 'core/StdTx', 45 | value: { 46 | msg: msg.map(m => m.toData()), 47 | fee: fee.toData(), 48 | signatures: signatures.map(s => s.toData()), 49 | memo, 50 | timeout_height: timeout_height.toFixed(), 51 | }, 52 | }; 53 | } 54 | } 55 | export namespace StdTx { 56 | export interface Data { 57 | type: 'core/StdTx'; 58 | value: { 59 | msg: Msg.Data[]; 60 | fee: StdFee.Data; 61 | signatures: StdSignature.Data[]; 62 | memo: string; 63 | timeout_height: string; 64 | }; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/core/staking/Redelegation.data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "redelegation": { 4 | "delegator_address": "terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v", 5 | "validator_src_address": "terravaloper14un92kpq0pvflyjd8sxmrsswglhn8mf5hkruc2", 6 | "validator_dst_address": "terravaloper18t8mtyvxlxf2gjh4x5r06gjfg0nk6gqs974uxh", 7 | "entries": null 8 | }, 9 | "entries": [ 10 | { 11 | "redelegation_entry": { 12 | "creation_height": 4288713, 13 | "completion_time": "2021-06-17T08:20:51.859505136Z", 14 | "initial_balance": "11999999", 15 | "shares_dst": "12121209.863287294700189881" 16 | }, 17 | "balance": "11999999" 18 | }, 19 | { 20 | "redelegation_entry": { 21 | "creation_height": 4288750, 22 | "completion_time": "2021-06-17T08:24:15.433539611Z", 23 | "initial_balance": "11000000", 24 | "shares_dst": "11111109.967272517414550508" 25 | }, 26 | "balance": "10999999" 27 | }, 28 | { 29 | "redelegation_entry": { 30 | "creation_height": 4289208, 31 | "completion_time": "2021-06-17T09:06:10.093949554Z", 32 | "initial_balance": "10000000", 33 | "shares_dst": "10101009.061156834013227735" 34 | }, 35 | "balance": "10000000" 36 | } 37 | ] 38 | }, 39 | { 40 | "redelegation": { 41 | "delegator_address": "terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v", 42 | "validator_src_address": "terravaloper14un92kpq0pvflyjd8sxmrsswglhn8mf5hkruc2", 43 | "validator_dst_address": "terravaloper1krj7amhhagjnyg2tkkuh6l0550y733jnjnnlzy", 44 | "entries": null 45 | }, 46 | "entries": [ 47 | { 48 | "redelegation_entry": { 49 | "creation_height": 4289212, 50 | "completion_time": "2021-06-17T09:06:31.632048465Z", 51 | "initial_balance": "5000000", 52 | "shares_dst": "5205622.236075405701241331" 53 | }, 54 | "balance": "5000000" 55 | } 56 | ] 57 | } 58 | ] -------------------------------------------------------------------------------- /src/core/auth/Account.ts: -------------------------------------------------------------------------------- 1 | import { Coins } from '../Coins'; 2 | import { PublicKey } from '../PublicKey'; 3 | import { JSONSerializable } from '../../util/json'; 4 | import { AccAddress } from '../bech32'; 5 | 6 | /** 7 | * Stores information about an account fetched from the blockchain. 8 | */ 9 | export class Account extends JSONSerializable { 10 | /** 11 | * Creates a new Account object, holding information about a basic account. 12 | * 13 | * @param address account address 14 | * @param coins account's balance 15 | * @param public_key account's public key information 16 | * @param account_number account number on the blockchain 17 | * @param sequence sequence number, or number of transactions that have been posted 18 | */ 19 | constructor( 20 | public address: AccAddress, 21 | public coins: Coins, 22 | public public_key: PublicKey | null, 23 | public account_number: number, 24 | public sequence: number 25 | ) { 26 | super(); 27 | } 28 | 29 | public toData(): Account.Data { 30 | const { address, coins, public_key, account_number, sequence } = this; 31 | return { 32 | type: 'core/Account', 33 | value: { 34 | address, 35 | coins: coins.toData(), 36 | public_key: public_key ? public_key.toData() : null, 37 | account_number: account_number.toFixed(), 38 | sequence: sequence.toFixed(), 39 | }, 40 | }; 41 | } 42 | 43 | public static fromData(data: Account.Data): Account { 44 | const { 45 | value: { address, coins, public_key, account_number, sequence }, 46 | } = data; 47 | 48 | return new Account( 49 | address || '', 50 | Coins.fromData(coins), 51 | public_key ? PublicKey.fromData(public_key) : null, 52 | Number.parseInt(account_number) || 0, 53 | Number.parseInt(sequence) || 0 54 | ); 55 | } 56 | } 57 | 58 | export namespace Account { 59 | export interface Value { 60 | address: AccAddress; 61 | coins: Coins.Data; 62 | public_key: PublicKey.Data | null; 63 | account_number: string; 64 | sequence: string; 65 | } 66 | 67 | export interface Data { 68 | type: 'core/Account'; 69 | value: Value; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/core/wasm/msgs/MsgInstantiateContract.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable, removeNull } from '../../../util/json'; 2 | import { AccAddress } from '../../bech32'; 3 | import { Coins } from '../../Coins'; 4 | 5 | export class MsgInstantiateContract extends JSONSerializable { 6 | public init_coins: Coins; 7 | 8 | /** 9 | * @param sender is a sender address 10 | * @param admin is an optional contract admin address who can migrate the contract, put empty string to disable migration 11 | * @param code_id is the reference to the stored WASM code 12 | * @param init_msg json encoded message to be passed to the contract on instantiation 13 | * @param init_coins are transferred to the contract on execution 14 | */ 15 | constructor( 16 | public sender: AccAddress, 17 | public admin: AccAddress | undefined, 18 | public code_id: number, 19 | public init_msg: object, 20 | init_coins: Coins.Input = {} 21 | ) { 22 | super(); 23 | this.init_coins = new Coins(init_coins); 24 | } 25 | 26 | public static fromData( 27 | data: MsgInstantiateContract.Data 28 | ): MsgInstantiateContract { 29 | const { 30 | value: { sender, admin, code_id, init_msg, init_coins }, 31 | } = data; 32 | return new MsgInstantiateContract( 33 | sender, 34 | admin, 35 | Number.parseInt(code_id), 36 | init_msg, 37 | Coins.fromData(init_coins) 38 | ); 39 | } 40 | 41 | public toData(): MsgInstantiateContract.Data { 42 | const { sender, admin, code_id, init_msg, init_coins } = this; 43 | return { 44 | type: 'wasm/MsgInstantiateContract', 45 | value: { 46 | sender, 47 | admin: admin === '' || admin === null ? undefined : admin, 48 | code_id: code_id.toFixed(), 49 | init_msg: removeNull(init_msg), 50 | init_coins: init_coins.toData(), 51 | }, 52 | }; 53 | } 54 | } 55 | 56 | export namespace MsgInstantiateContract { 57 | export interface Data { 58 | type: 'wasm/MsgInstantiateContract'; 59 | value: { 60 | sender: AccAddress; 61 | admin?: AccAddress; 62 | code_id: string; 63 | init_msg: object; 64 | init_coins: Coins.Data; 65 | }; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/core/staking/msgs/MsgBeginRedelegate.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { Coin } from '../../Coin'; 3 | import { AccAddress, ValAddress } from '../../bech32'; 4 | 5 | /** 6 | * A delegator can choose to redelegate their bonded Luna and transfer a delegation 7 | * amount from one validator to another. Unlike undelegating, redelegations do not incur 8 | * a 21-day unbonding period and happen immediately. 9 | */ 10 | export class MsgBeginRedelegate extends JSONSerializable { 11 | /** 12 | * 13 | * @param delegator_address delegator's account address 14 | * @param validator_src_address validator to undelegate from 15 | * @param validator_dst_address validator to delegate to 16 | * @param amount LUNA to be redelegated 17 | */ 18 | constructor( 19 | public delegator_address: AccAddress, 20 | public validator_src_address: ValAddress, 21 | public validator_dst_address: ValAddress, 22 | public amount: Coin 23 | ) { 24 | super(); 25 | } 26 | 27 | public static fromData(data: MsgBeginRedelegate.Data): MsgBeginRedelegate { 28 | const { 29 | value: { 30 | delegator_address, 31 | validator_src_address, 32 | validator_dst_address, 33 | amount, 34 | }, 35 | } = data; 36 | return new MsgBeginRedelegate( 37 | delegator_address, 38 | validator_src_address, 39 | validator_dst_address, 40 | Coin.fromData(amount) 41 | ); 42 | } 43 | 44 | public toData(): MsgBeginRedelegate.Data { 45 | const { 46 | delegator_address, 47 | validator_src_address, 48 | validator_dst_address, 49 | amount, 50 | } = this; 51 | return { 52 | type: 'staking/MsgBeginRedelegate', 53 | value: { 54 | delegator_address, 55 | validator_src_address, 56 | validator_dst_address, 57 | amount: amount.toData(), 58 | }, 59 | }; 60 | } 61 | } 62 | 63 | export namespace MsgBeginRedelegate { 64 | export interface Data { 65 | type: 'staking/MsgBeginRedelegate'; 66 | value: { 67 | delegator_address: AccAddress; 68 | validator_src_address: ValAddress; 69 | validator_dst_address: ValAddress; 70 | amount: Coin.Data; 71 | }; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /integration-tests/contract.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MsgStoreCode, 3 | MsgInstantiateContract, 4 | MsgExecuteContract, 5 | StdFee, 6 | isTxError, 7 | LocalTerra, 8 | getCodeId, 9 | getContractAddress, 10 | } from '../src'; 11 | import * as fs from 'fs'; 12 | 13 | // test1 key from localterra accounts 14 | const terra = new LocalTerra(); 15 | const { test1 } = terra.wallets; 16 | 17 | async function main(): Promise { 18 | const storeCode = new MsgStoreCode( 19 | test1.key.accAddress, 20 | fs.readFileSync('contract.wasm').toString('base64') 21 | ); 22 | const storeCodeTx = await test1.createAndSignTx({ 23 | msgs: [storeCode], 24 | }); 25 | const storeCodeTxResult = await terra.tx.broadcast(storeCodeTx); 26 | 27 | console.log(storeCodeTxResult); 28 | 29 | if (isTxError(storeCodeTxResult)) { 30 | throw new Error( 31 | `store code failed. code: ${storeCodeTxResult.code}, codespace: ${storeCodeTxResult.codespace}, raw_log: ${storeCodeTxResult.raw_log}` 32 | ); 33 | } 34 | 35 | const codeId = getCodeId(storeCodeTxResult); 36 | 37 | const instantiate = new MsgInstantiateContract( 38 | test1.key.accAddress, 39 | null, 40 | +codeId, // code ID 41 | { count: 0, }, // InitMsg 42 | { uluna: 10000000, ukrw: 1000000 } // init coins 43 | ); 44 | 45 | const instantiateTx = await test1.createAndSignTx({ 46 | msgs: [instantiate], 47 | }); 48 | const instantiateTxResult = await terra.tx.broadcast(instantiateTx); 49 | 50 | console.log(instantiateTxResult); 51 | 52 | if (isTxError(instantiateTxResult)) { 53 | throw new Error( 54 | `instantiate failed. code: ${instantiateTxResult.code}, codespace: ${instantiateTxResult.codespace}, raw_log: ${instantiateTxResult.raw_log}` 55 | ); 56 | } 57 | 58 | const contractAddress = getContractAddress(instantiateTxResult); 59 | 60 | const execute = new MsgExecuteContract( 61 | test1.key.accAddress, // sender 62 | contractAddress, // contract address 63 | { increment: {} }, // handle msg 64 | { uluna: 100000 } // coins 65 | ); 66 | const executeTx = await test1.createAndSignTx({ 67 | msgs: [execute], 68 | }); 69 | const executeTxResult = await terra.tx.broadcast(executeTx); 70 | console.log(executeTxResult); 71 | } 72 | 73 | main().then(console.log); 74 | -------------------------------------------------------------------------------- /src/client/lcd/api/MarketAPI.ts: -------------------------------------------------------------------------------- 1 | import { Coin, Dec, Numeric, Denom } from '../../../core'; 2 | import { APIParams } from '../APIRequester'; 3 | import { BaseAPI } from './BaseAPI'; 4 | 5 | export interface MarketParams { 6 | /** Number of blocks it takes for the Terra & Luna pools to naturally "reset" towards 7 | * equilibrium through automated pool replenishing. 8 | */ 9 | pool_recovery_period: number; 10 | 11 | /** Initial starting size of both Luna and Terra mint liquidity pools. */ 12 | base_pool: Dec; 13 | 14 | /** Minimum spread charged on Terra<>Luna swaps to prevent leaking value from front-running attacks. */ 15 | min_stability_spread: Dec; 16 | } 17 | 18 | export namespace MarketParams { 19 | export interface Data { 20 | pool_recovery_period: string; 21 | base_pool: string; 22 | min_stability_spread: string; 23 | } 24 | } 25 | 26 | export class MarketAPI extends BaseAPI { 27 | /** 28 | * Gets the Market's swap rate for a given coin to a requested denomination. 29 | * @param offerCoin coin to convert 30 | * @param askDenom denomination to swap into 31 | */ 32 | public async swapRate( 33 | offerCoin: Coin, 34 | askDenom: Denom, 35 | _params: APIParams = {} 36 | ): Promise { 37 | const params = { 38 | ..._params, 39 | offer_coin: offerCoin.toString(), 40 | ask_denom: askDenom, 41 | }; 42 | 43 | return this.c 44 | .get(`/market/swap`, params) 45 | .then(d => Coin.fromData(d.result)); 46 | } 47 | 48 | /** 49 | * Gets current value of the pool delta, which is used to determine Terra<>Luna swap rates. 50 | */ 51 | public async poolDelta(params: APIParams = {}): Promise { 52 | return this.c 53 | .get(`/market/terra_pool_delta`, params) 54 | .then(d => new Dec(d.result)); 55 | } 56 | 57 | /** 58 | * Gets the current Market module's parameters. 59 | */ 60 | public async parameters(params: APIParams = {}): Promise { 61 | return this.c 62 | .get(`/market/parameters`, params) 63 | .then(({ result: d }) => ({ 64 | pool_recovery_period: Number.parseInt(d.pool_recovery_period), 65 | base_pool: new Dec(d.base_pool), 66 | min_stability_spread: new Dec(d.min_stability_spread), 67 | })); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/core/slashing/params.ts: -------------------------------------------------------------------------------- 1 | import { ParamChange } from '../params/ParamChange'; 2 | import { Convert } from '../../util/convert'; 3 | import { Dec } from '../numeric'; 4 | 5 | export type MaxEvidenceAge = ParamChange.Type< 6 | 'slashing', 7 | 'MaxEvidenceAge', 8 | number 9 | >; 10 | 11 | export type SignedBlocksWindow = ParamChange.Type< 12 | 'slashing', 13 | 'SignedBlocksWindow', 14 | number 15 | >; 16 | 17 | export type MinSignedPerWindow = ParamChange.Type< 18 | 'slashing', 19 | 'MinSignedPerWindow', 20 | Dec 21 | >; 22 | 23 | export type DowntimeJailDuration = ParamChange.Type< 24 | 'slashing', 25 | 'DowntimeJailDuration', 26 | number 27 | >; 28 | 29 | export type SlashFractionDoubleSign = ParamChange.Type< 30 | 'slashing', 31 | 'SlashFractionDoubleSign', 32 | Dec 33 | >; 34 | 35 | export type SlashFractionDowntime = ParamChange.Type< 36 | 'slashing', 37 | 'SlashFractionDowntime', 38 | Dec 39 | >; 40 | 41 | export type SlashingParamChange = 42 | | MaxEvidenceAge 43 | | SignedBlocksWindow 44 | | MinSignedPerWindow 45 | | DowntimeJailDuration 46 | | SlashFractionDoubleSign 47 | | SlashFractionDowntime; 48 | 49 | export namespace SlashingParamChange { 50 | export type Data = 51 | | ParamChange.Data.Type 52 | | ParamChange.Data.Type 53 | | ParamChange.Data.Type 54 | | ParamChange.Data.Type 55 | | ParamChange.Data.Type 56 | | ParamChange.Data.Type; 57 | } 58 | 59 | export interface SlashingParamChanges { 60 | slashing?: { 61 | MaxEvidenceAge?: number; 62 | SignedBlocksWindow?: number; 63 | MinSignedPerWindow?: Dec; 64 | DowntimeJailDuration?: number; 65 | SlashFractionDoubleSign?: Dec; 66 | SlashFractionDowntime?: Dec; 67 | }; 68 | } 69 | 70 | export namespace SlashingParamChanges { 71 | export const ConversionTable = { 72 | slashing: { 73 | MaxEvidenceAge: [Convert.toNumber, Convert.toFixed], 74 | SignedBlocksWindow: [Convert.toNumber, Convert.toFixed], 75 | MinSignedPerWindow: [Convert.toDec, Convert.toString], 76 | DowntimeJailDuration: [Convert.toNumber, Convert.toString], 77 | SlashFractionDoubleSign: [Convert.toDec, Convert.toString], 78 | SlashFractionDowntime: [Convert.toDec, Convert.toString], 79 | }, 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /src/util/contract.ts: -------------------------------------------------------------------------------- 1 | import { BlockTxBroadcastResult, isTxError } from '../client/lcd/api/TxAPI'; 2 | import { TxInfo } from '../core/TxInfo'; 3 | 4 | export function getCodeId( 5 | txResult: BlockTxBroadcastResult | TxInfo, 6 | msgIndex = 0 7 | ): string { 8 | if ( 9 | isTxError(txResult) || 10 | txResult.logs === undefined || 11 | txResult.logs.length === 0 12 | ) { 13 | throw new Error('could not parse code id -- tx logs are empty.'); 14 | } 15 | const codeId = 16 | txResult.logs[msgIndex].eventsByType['store_code']['code_id'][0]; 17 | return codeId; 18 | } 19 | 20 | export function getContractAddress( 21 | txResult: BlockTxBroadcastResult | TxInfo, 22 | msgIndex = 0 23 | ): string { 24 | if ( 25 | isTxError(txResult) || 26 | txResult.logs === undefined || 27 | txResult.logs.length === 0 28 | ) { 29 | throw new Error('could not parse contract address -- tx logs are empty.'); 30 | } 31 | const contractAddress = 32 | txResult.logs[msgIndex].eventsByType['instantiate_contract'][ 33 | 'contract_address' 34 | ][0]; 35 | return contractAddress; 36 | } 37 | 38 | export interface ContractEvent { 39 | contract_address: string; 40 | [key: string]: string; 41 | } 42 | 43 | export function getContractEvents( 44 | txResult: BlockTxBroadcastResult | TxInfo, 45 | msgIndex = 0 46 | ): ContractEvent[] { 47 | if ( 48 | isTxError(txResult) || 49 | txResult.logs === undefined || 50 | txResult.logs.length === 0 51 | ) { 52 | throw new Error('could not parse contract events -- tx logs are empty.'); 53 | } 54 | const contractEvents: ContractEvent[] = []; 55 | for (const event of txResult.logs[msgIndex].events) { 56 | if (event.type === 'from_contract') { 57 | let eventData: ContractEvent = { contract_address: '' }; // will be overwritten 58 | let currentContractAddress = event.attributes[0].value; 59 | for (const att of event.attributes) { 60 | if ( 61 | att.key == 'contract_address' && 62 | currentContractAddress !== att.value 63 | ) { 64 | contractEvents.push(eventData); 65 | eventData = { contract_address: '' }; 66 | currentContractAddress = att.value; 67 | } 68 | eventData[att.key] = att.value; 69 | } 70 | contractEvents.push(eventData); 71 | return contractEvents; 72 | } 73 | } 74 | throw new Error("could not find event type 'from_contract' in logs"); 75 | } 76 | -------------------------------------------------------------------------------- /src/core/treasury/params.ts: -------------------------------------------------------------------------------- 1 | import { ParamChange } from '../params/ParamChange'; 2 | import { PolicyConstraints } from './PolicyConstraints'; 3 | import { Convert } from '../../util/convert'; 4 | import { Dec } from '../numeric'; 5 | 6 | export type TaxPolicy = ParamChange.Type< 7 | 'treasury', 8 | 'TaxPolicy', 9 | PolicyConstraints 10 | >; 11 | export type RewardPolicy = ParamChange.Type< 12 | 'treasury', 13 | 'RewardPolicy', 14 | PolicyConstraints 15 | >; 16 | export type SeigniorageBurdenTarget = ParamChange.Type< 17 | 'treasury', 18 | 'SeigniorageBurdenTarget', 19 | Dec 20 | >; 21 | export type MiningIncrement = ParamChange.Type< 22 | 'treasury', 23 | 'MiningIncrement', 24 | Dec 25 | >; 26 | export type WindowShort = ParamChange.Type<'treasury', 'WindowShort', number>; 27 | export type WindowLong = ParamChange.Type<'treasury', 'WindowLong', number>; 28 | export type WindowProbation = ParamChange.Type< 29 | 'treasury', 30 | 'WindowProbation', 31 | number 32 | >; 33 | 34 | export type TreasuryParamChange = 35 | | TaxPolicy 36 | | RewardPolicy 37 | | SeigniorageBurdenTarget 38 | | MiningIncrement 39 | | WindowShort 40 | | WindowLong 41 | | WindowProbation; 42 | 43 | export namespace TreasuryParamChange { 44 | export type Data = 45 | | ParamChange.Data.Type 46 | | ParamChange.Data.Type 47 | | ParamChange.Data.Type 48 | | ParamChange.Data.Type 49 | | ParamChange.Data.Type 50 | | ParamChange.Data.Type 51 | | ParamChange.Data.Type; 52 | } 53 | 54 | export interface TreasuryParamChanges { 55 | treasury?: { 56 | TaxPolicy?: PolicyConstraints; 57 | RewardPolicy?: PolicyConstraints; 58 | SeigniorageBurdenTarget?: Dec; 59 | MiningIncrement?: Dec; 60 | WindowShort?: number; 61 | WindowLong?: number; 62 | WindowProbation?: number; 63 | }; 64 | } 65 | 66 | export namespace TreasuryParamChanges { 67 | export const ConversionTable = { 68 | treasury: { 69 | TaxPolicy: [PolicyConstraints.fromData, Convert.toData], 70 | RewardPolicy: [PolicyConstraints.fromData, Convert.toData], 71 | SeigniorageBurdenTarget: [Convert.toDec, Convert.toString], 72 | MiningIncrement: [Convert.toDec, Convert.toString], 73 | WindowShort: [Convert.toNumber, Convert.toFixed], 74 | WindowLong: [Convert.toNumber, Convert.toFixed], 75 | WindowProbation: [Convert.toNumber, Convert.toFixed], 76 | }, 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /src/core/auth/Account.data.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "type": "core/Account", 3 | "value": { 4 | "address": "terra1h0d5kq5p64jcyqysvja3h2gysxnfudk9h73fnn", 5 | "coins": [{ 6 | "denom": "ukrw", 7 | "amount": "3981685437" 8 | }, 9 | { 10 | "denom": "uluna", 11 | "amount": "2041" 12 | }, 13 | { 14 | "denom": "umnt", 15 | "amount": "363613" 16 | } 17 | ], 18 | "public_key": { 19 | "type": "tendermint/PubKeySecp256k1", 20 | "value": "Av0uQ9R72aq0dgu1H+F1+1p2daQLl0JsWhwkA07ftTq/" 21 | }, 22 | "account_number": "572696", 23 | "sequence": "251409" 24 | } 25 | }, 26 | { 27 | "type": "core/Account", 28 | "value": { 29 | "address": "terra1fex9f78reuwhfsnc8sun6mz8rl9zwqh03fhwf3", 30 | "coins": [{ 31 | "denom": "ukrw", 32 | "amount": "15464712273121303" 33 | }], 34 | "public_key": { 35 | "type": "tendermint/PubKeySecp256k1", 36 | "value": "AxYEJ7m2BMIzrugTG5Rl8Pz2RNU6eGfnsn2TfMXWEruf" 37 | }, 38 | "account_number": "238665", 39 | "sequence": "1650113" 40 | } 41 | }, 42 | { 43 | "type": "core/Account", 44 | "value": { 45 | "address": "terra12fm3tql2uu0gheuj3st9cwz7ml97tq9mla88c2", 46 | "coins": [{ 47 | "denom": "ukrw", 48 | "amount": "1077000000" 49 | }], 50 | "public_key": { 51 | "type": "tendermint/PubKeySecp256k1", 52 | "value": "AvBeqhogW0wd7OtF8M8hJ/P1A/IBY1+uNvBO/tbVlfq2" 53 | }, 54 | "account_number": "251248", 55 | "sequence": "58" 56 | } 57 | }, 58 | { 59 | "type": "core/Account", 60 | "value": { 61 | "address": "terra1kcc65k0ru6qq946ztqljxd67d5wltqstln5d5k", 62 | "coins": [], 63 | "public_key": { 64 | "type": "tendermint/PubKeySecp256k1", 65 | "value": "AsPdLO1QwuajkAP/MlVe17WwTimT7gUAE2iXDjaScrxH" 66 | }, 67 | "account_number": "987828", 68 | "sequence": "28" 69 | } 70 | }, 71 | { 72 | "type": "core/Account", 73 | "value": { 74 | "address": "terra1ax7xtll5v6u6vdnymxa4k4648w80zhkggl0u24", 75 | "coins": [{ 76 | "denom": "ukrw", 77 | "amount": "31999000000" 78 | }], 79 | "public_key": { 80 | "type": "tendermint/PubKeySecp256k1", 81 | "value": "A/vzuSK9wCAvmFXorv/KTbUFsF9Av5r9XuplqF+4OLfR" 82 | }, 83 | "account_number": "712869", 84 | "sequence": "65" 85 | } 86 | } 87 | ] -------------------------------------------------------------------------------- /src/key/MnemonicKey.ts: -------------------------------------------------------------------------------- 1 | // Adapted from https://github.com/terra-money/terra-js/blob/master/src/utils/keyUtils.ts 2 | 3 | import * as bip32 from 'bip32'; 4 | import * as bip39 from 'bip39'; 5 | import { RawKey } from './RawKey'; 6 | 7 | export const LUNA_COIN_TYPE = 330; 8 | 9 | interface MnemonicKeyOptions { 10 | /** 11 | * Space-separated list of words for the mnemonic key. 12 | */ 13 | mnemonic?: string; 14 | 15 | /** 16 | * BIP44 account number. 17 | */ 18 | account?: number; 19 | 20 | /** 21 | * BIP44 index number 22 | */ 23 | index?: number; 24 | 25 | /** 26 | * Coin type. Default is LUNA, 330. 27 | */ 28 | coinType?: number; 29 | } 30 | 31 | const DEFAULT_OPTIONS = { 32 | account: 0, 33 | index: 0, 34 | coinType: LUNA_COIN_TYPE, 35 | }; 36 | 37 | /** 38 | * Implements a BIP39 mnemonic wallet with standard key derivation from a word list. Note 39 | * that this implementation exposes the private key in memory, so it is not advised to use 40 | * for applications requiring high security. 41 | */ 42 | export class MnemonicKey extends RawKey { 43 | /** 44 | * Space-separated mnemonic phrase. 45 | */ 46 | public mnemonic: string; 47 | 48 | /** 49 | * Creates a new signing key from a mnemonic phrase. If no mnemonic is provided, one 50 | * will be automatically generated. 51 | * 52 | * ### Providing a mnemonic 53 | * 54 | * ```ts 55 | * import { MnemonicKey } from 'terra.js'; 56 | * 57 | * const mk = new MnemonicKey({ mnemonic: '...' }); 58 | * console.log(mk.accAddress); 59 | * ``` 60 | * 61 | * ### Generating a random mnemonic 62 | * 63 | * ```ts 64 | * const mk2 = new MnemonicKey(); 65 | * console.log(mk2.mnemonic); 66 | * ``` 67 | * 68 | * @param options 69 | */ 70 | constructor(options: MnemonicKeyOptions = {}) { 71 | const { account, index, coinType } = { 72 | ...DEFAULT_OPTIONS, 73 | ...options, 74 | }; 75 | let { mnemonic } = options; 76 | if (mnemonic === undefined) { 77 | mnemonic = bip39.generateMnemonic(256); 78 | } 79 | const seed: Buffer = bip39.mnemonicToSeedSync(mnemonic); 80 | const masterKey = bip32.fromSeed(seed); 81 | const hdPathLuna = `m/44'/${coinType}'/${account}'/0/${index}`; 82 | const terraHD = masterKey.derivePath(hdPathLuna); 83 | const privateKey = terraHD.privateKey; 84 | 85 | if (!privateKey) { 86 | throw new Error('Failed to derive key pair'); 87 | } 88 | 89 | super(privateKey); 90 | this.mnemonic = mnemonic; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/core/gov/Vote.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../util/json'; 2 | import { AccAddress } from '../bech32'; 3 | 4 | /** 5 | * Stores vote information for a proposal 6 | */ 7 | export class Vote extends JSONSerializable { 8 | /** 9 | * @param proposal_id ID of proposal to vote on 10 | * @param voter voter's account address 11 | * @param option one of voting options 12 | */ 13 | constructor( 14 | public proposal_id: number, 15 | public voter: AccAddress, 16 | public options: Vote.Options, 17 | public option?: Vote.Option // undefined except proposals in voting status 18 | ) { 19 | super(); 20 | } 21 | 22 | public static fromData(data: Vote.Data): Vote { 23 | const { proposal_id, voter, options, option } = data; 24 | 25 | return new Vote( 26 | Number.parseInt(proposal_id), 27 | voter, 28 | options.map(({ option, weight }) => ({ 29 | option: Vote.OptionMapping[option], 30 | weight, 31 | })), 32 | option ? Vote.OptionMapping[option] : undefined 33 | ); 34 | } 35 | 36 | public toData(): Vote.Data { 37 | const { proposal_id, voter, options, option } = this; 38 | 39 | const res: Vote.Data = { 40 | proposal_id: proposal_id.toFixed(), 41 | voter, 42 | options: options.map(({ option, weight }) => ({ 43 | option: Object.keys(Vote.OptionMapping).indexOf(option), 44 | weight, 45 | })), 46 | }; 47 | 48 | if (option) { 49 | res.option = Object.keys(Vote.OptionMapping).indexOf(option); 50 | } 51 | 52 | return res; 53 | } 54 | } 55 | 56 | export namespace Vote { 57 | export type Options = { 58 | option: Option; 59 | weight: string; 60 | }[]; 61 | 62 | /** Voting options */ 63 | export enum Option { 64 | /** - */ 65 | EMPTY = 'Empty', 66 | 67 | /** Vote yes */ 68 | YES = 'Yes', 69 | 70 | /** Do not vote */ 71 | ABSTAIN = 'Abstain', 72 | 73 | /** Vote no */ 74 | NO = 'No', 75 | 76 | /** Vote No with the option to veto if passed */ 77 | NO_WITH_VETO = 'NoWithVeto', 78 | } 79 | 80 | export const OptionMapping: { [key: number]: Option } = { 81 | 0: Option.EMPTY, 82 | 1: Option.YES, 83 | 2: Option.ABSTAIN, 84 | 3: Option.NO, 85 | 4: Option.NO_WITH_VETO, 86 | }; 87 | 88 | export interface Data { 89 | proposal_id: string; 90 | voter: AccAddress; 91 | option?: number; // undefined except proposals in voting status 92 | options: { 93 | option: number; 94 | weight: string; // Dec 95 | }[]; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/client/lcd/api/WasmAPI.ts: -------------------------------------------------------------------------------- 1 | import { BaseAPI } from './BaseAPI'; 2 | import { AccAddress } from '../../../core/bech32'; 3 | import { APIParams } from '../APIRequester'; 4 | 5 | export interface CodeInfo { 6 | code_hash: string; 7 | creator: AccAddress; 8 | } 9 | 10 | export interface ContractInfo { 11 | code_id: number; 12 | address: AccAddress; 13 | creator: AccAddress; 14 | admin: AccAddress; 15 | init_msg: any; // object 16 | } 17 | 18 | export namespace ContractInfo { 19 | export interface Data { 20 | code_id: string; 21 | address: AccAddress; 22 | creator: AccAddress; 23 | admin: AccAddress; 24 | init_msg: any; // object 25 | } 26 | } 27 | 28 | export interface WasmParams { 29 | max_contract_size: number; 30 | max_contract_gas: number; 31 | max_contract_msg_size: number; 32 | } 33 | 34 | export namespace WasmParams { 35 | export interface Data { 36 | max_contract_size: string; 37 | max_contract_gas: string; 38 | max_contract_msg_size: string; 39 | } 40 | } 41 | 42 | export class WasmAPI extends BaseAPI { 43 | public async codeInfo( 44 | codeID: number, 45 | params: APIParams = {} 46 | ): Promise { 47 | return this.c 48 | .get(`/wasm/codes/${codeID}`, params) 49 | .then(d => d.result); 50 | } 51 | 52 | public async contractInfo( 53 | contractAddress: AccAddress, 54 | params: APIParams = {} 55 | ): Promise { 56 | return this.c 57 | .get(`/wasm/contracts/${contractAddress}`, params) 58 | .then(({ result: d }) => ({ 59 | code_id: Number.parseInt(d.code_id), 60 | address: d.address, 61 | creator: d.creator, 62 | admin: d.admin, 63 | init_msg: d.init_msg, 64 | })); 65 | } 66 | 67 | public async contractQuery( 68 | contractAddress: AccAddress, 69 | query: object, 70 | params: APIParams = {} 71 | ): Promise { 72 | return this.c 73 | .get(`/wasm/contracts/${contractAddress}/store`, { 74 | ...params, 75 | query_msg: JSON.stringify(query), 76 | }) 77 | .then(d => d.result); 78 | } 79 | 80 | public async parameters(params: APIParams = {}): Promise { 81 | return this.c 82 | .get(`/wasm/parameters`, params) 83 | .then(({ result: d }) => ({ 84 | max_contract_size: Number.parseInt(d.max_contract_size), 85 | max_contract_gas: Number.parseInt(d.max_contract_gas), 86 | max_contract_msg_size: Number.parseInt(d.max_contract_msg_size), 87 | })); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/core/staking/Validator.spec.ts: -------------------------------------------------------------------------------- 1 | import { Validator } from './Validator'; 2 | import { Int, Dec } from '../numeric'; 3 | 4 | describe('Validator', () => { 5 | it('deserializes', () => { 6 | const validatorData = { 7 | operator_address: 'terravaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvgk6d65', 8 | consensus_pubkey: { 9 | type: 'tendermint/PubKeyEd25519', 10 | value: '1v2BCLSLYe9tQ9JXMuYURf3UIQ/uE+RUVcYfTDVM1ec=', 11 | }, 12 | jailed: false, 13 | status: 2, 14 | tokens: '111401100001', 15 | delegator_shares: '111401100001.000000000000000000', 16 | description: { 17 | moniker: 'WeStaking', 18 | identity: 'DA9C5AD3E308E426', 19 | website: 'https://www.westaking.io', 20 | details: 21 | 'Delegate your luna to us for the staking rewards. We will do our best as secure and stable validator.', 22 | security_contact: 'x@x.com', 23 | }, 24 | unbonding_height: '0', 25 | unbonding_time: '1970-01-01T00:00:00Z', 26 | commission: { 27 | commission_rates: { 28 | rate: '0.200000000000000000', 29 | max_rate: '0.250000000000000000', 30 | max_change_rate: '0.010000000000000000', 31 | }, 32 | update_time: '2019-12-01T03:28:34.024363013Z', 33 | }, 34 | min_self_delegation: '1', 35 | }; 36 | const validator = Validator.fromData(validatorData); 37 | expect(validator).toMatchObject({ 38 | operator_address: 'terravaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvgk6d65', 39 | consensus_pubkey: { 40 | type: 'tendermint/PubKeyEd25519', 41 | value: '1v2BCLSLYe9tQ9JXMuYURf3UIQ/uE+RUVcYfTDVM1ec=', 42 | }, 43 | jailed: false, 44 | status: 2, 45 | tokens: new Int(111401100001), 46 | delegator_shares: new Dec('111401100001.000000000000000000'), 47 | description: { 48 | moniker: 'WeStaking', 49 | identity: 'DA9C5AD3E308E426', 50 | website: 'https://www.westaking.io', 51 | details: 52 | 'Delegate your luna to us for the staking rewards. We will do our best as secure and stable validator.', 53 | security_contact: 'x@x.com', 54 | }, 55 | unbonding_height: 0, 56 | unbonding_time: new Date('1970-01-01T00:00:00Z'), 57 | commission: { 58 | commission_rates: { 59 | rate: new Dec('0.2'), 60 | max_rate: new Dec('0.25'), 61 | max_change_rate: new Dec('0.01'), 62 | }, 63 | update_time: new Date('2019-12-01T03:28:34.024363013Z'), 64 | }, 65 | min_self_delegation: new Int(1), 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /src/extension/PostMessageStream.ts: -------------------------------------------------------------------------------- 1 | import { Duplex } from 'readable-stream'; 2 | 3 | const noop = () => { 4 | return undefined; 5 | }; 6 | 7 | interface Options { 8 | name: string; 9 | target: string; 10 | targetWindow?: Window; 11 | } 12 | 13 | interface Message { 14 | target: string; 15 | data: any; 16 | } 17 | 18 | export default class PostMessageStream extends Duplex { 19 | private _target: string; 20 | private _name: string; 21 | private _targetWindow: Window; 22 | private _origin: string; 23 | private _init: boolean; 24 | private _haveSyn: boolean; 25 | 26 | constructor({ name, target, targetWindow }: Options) { 27 | super({ objectMode: true }); 28 | 29 | this._name = name; 30 | this._target = target; 31 | this._targetWindow = targetWindow || window; 32 | this._origin = targetWindow ? '*' : location.origin; 33 | 34 | // initialization flags 35 | this._init = false; 36 | this._haveSyn = false; 37 | this._onMessage = this._onMessage.bind(this); 38 | 39 | window.addEventListener('message', this._onMessage as any, false); 40 | // send syncorization message 41 | this._write('SYN', null, noop); 42 | this.cork(); 43 | } 44 | 45 | _destroy() { 46 | // console.log('PostMessageStream: destroy'); 47 | window.removeEventListener('message', this._onMessage as any, false); 48 | } 49 | 50 | _onMessage(event: { source: Window; origin: string; data: Message }) { 51 | const msg = event.data as Message; 52 | 53 | // validate message 54 | if (this._origin !== '*' && event.origin !== this._origin) return; 55 | if (event.source !== this._targetWindow) return; 56 | if (typeof msg !== 'object') return; 57 | if (msg.target !== this._name) return; 58 | if (!msg.data) return; 59 | 60 | if (!this._init) { 61 | if (msg.data === 'SYN') { 62 | this._haveSyn = true; 63 | this._write('ACK', null, noop); 64 | } else if (msg.data === 'ACK') { 65 | this._init = true; 66 | if (!this._haveSyn) { 67 | this._write('ACK', null, noop); 68 | } 69 | this.uncork(); 70 | } 71 | } else { 72 | // forward message 73 | try { 74 | this.push(msg.data); 75 | } catch (err) { 76 | this.emit('error', err); 77 | } 78 | } 79 | } 80 | 81 | _read() { 82 | return undefined; 83 | } 84 | 85 | _write( 86 | data: any, 87 | _encoding: null | string, 88 | cb: (error?: Error | null) => void 89 | ) { 90 | const message = { 91 | target: this._target, 92 | data: data, 93 | }; 94 | this._targetWindow.postMessage(message, this._origin); 95 | cb(null); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@terra-money/terra.js", 3 | "version": "2.0.8", 4 | "description": "The JavaScript SDK for Terra", 5 | "license": "MIT", 6 | "author": "Terraform Labs, PTE.", 7 | "keywords": [ 8 | "terra", 9 | "stablecoin", 10 | "cryptocurrency", 11 | "blockchain", 12 | "smart-contracts", 13 | "wasm", 14 | "altcoin", 15 | "altcoins", 16 | "wallet" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "git://github.com/terra-money/terra.js.git" 21 | }, 22 | "main": "dist/index.js", 23 | "typings": "dist/index.d.ts", 24 | "files": [ 25 | "dist" 26 | ], 27 | "engines": { 28 | "node": ">=12" 29 | }, 30 | "scripts": { 31 | "build": "tsc --module commonjs && webpack --mode production", 32 | "test": "jest", 33 | "prettier": "prettier --write ./src/**/*.ts", 34 | "lint": "eslint src --ext .js,.jsx,.ts,.tsx", 35 | "doc": "typedoc", 36 | "prepublishOnly": "npm run build" 37 | }, 38 | "lint-staged": { 39 | "./src/**/*.ts": [ 40 | "prettier --write", 41 | "eslint" 42 | ] 43 | }, 44 | "husky": { 45 | "hooks": { 46 | "pre-commit": "lint-staged", 47 | "post-checkout": "npm i" 48 | } 49 | }, 50 | "prettier": { 51 | "semi": true, 52 | "singleQuote": true, 53 | "trailingComma": "es5", 54 | "arrowParens": "avoid" 55 | }, 56 | "devDependencies": { 57 | "@types/jest": "^27.0.1", 58 | "@types/node": "^15.12.4", 59 | "@types/readable-stream": "^2.3.9", 60 | "@types/secp256k1": "^4.0.1", 61 | "@types/tmp": "^0.2.0", 62 | "@types/ws": "^7.4.0", 63 | "@typescript-eslint/eslint-plugin": "^4.11.1", 64 | "@typescript-eslint/parser": "^4.11.1", 65 | "buffer": "^6.0.3", 66 | "eslint": "^7.16.0", 67 | "husky": "^4.3.6", 68 | "jest": "^27.0.4", 69 | "lint-staged": "^10.5.3", 70 | "lodash.memoize": "^4.1.2", 71 | "prettier": "^2.2.1", 72 | "stream-browserify": "^3.0.0", 73 | "ts-jest": "^27.0.5", 74 | "ts-loader": "^9.2.3", 75 | "ts-node": "^10.0.0", 76 | "tsconfig-paths-webpack-plugin": "^3.3.0", 77 | "typedoc": "^0.21.0", 78 | "typescript": "^4.3.4", 79 | "webpack": "^5.11.1", 80 | "webpack-bundle-analyzer": "^4.3.0", 81 | "webpack-cli": "^4.3.0" 82 | }, 83 | "dependencies": { 84 | "axios": "^0.21.1", 85 | "bech32": "^2.0.0", 86 | "bip32": "^2.0.6", 87 | "bip39": "^3.0.3", 88 | "bufferutil": "^4.0.3", 89 | "decimal.js": "^10.2.1", 90 | "jscrypto": "^1.0.1", 91 | "readable-stream": "^3.6.0", 92 | "secp256k1": "^4.0.2", 93 | "tmp": "^0.2.1", 94 | "utf-8-validate": "^5.0.5", 95 | "ws": "^7.4.2" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/core/params/proposals/ParameterChangeProposal.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { ParamChange, ParamChanges } from '../ParamChange'; 3 | 4 | /** 5 | * Describes a proposal for directly altering the value of the module parameters. 6 | * If you want to select a couple parameters to change for your proposal, you'll first 7 | * include the subspace (module it belongs to, such as "oracle" or "distribution"), and 8 | * then just the specific keys that you want to include in your changes as items in a 9 | * JavaScript object. 10 | * 11 | * ```ts 12 | * import { 13 | * Dec, 14 | * MsgSubmitProposal, 15 | * ParameterChangeProposal 16 | * } from "@terra-money/terra.js"; 17 | * 18 | * const proposal = new ParameterChangeProposal("title", "description", { 19 | * market: { 20 | * minspread: new Dec(0.25), 21 | * basepool: new Dec(10000000) 22 | * }, 23 | * staking: { 24 | * UnbondingTime: 15000000 25 | * } 26 | * }); 27 | * 28 | * const msg = new MsgSubmitProposal(); 29 | * ``` 30 | */ 31 | export class ParameterChangeProposal extends JSONSerializable< 32 | ParameterChangeProposal.Data 33 | > { 34 | changes: ParamChanges; 35 | 36 | /** 37 | * @param title proposal's title 38 | * @param description proposal's description 39 | * @param changes an object whose keys are subspace names, and whose values are objects 40 | * with objects having for keys and values, the desired parameter changes. 41 | */ 42 | constructor( 43 | public title: string, 44 | public description: string, 45 | changes: ParamChange.Data[] | ParamChanges 46 | ) { 47 | super(); 48 | if (changes instanceof Array) { 49 | this.changes = ParamChanges.fromData(changes); 50 | } else { 51 | this.changes = changes; 52 | } 53 | } 54 | 55 | public static fromData( 56 | data: ParameterChangeProposal.Data 57 | ): ParameterChangeProposal { 58 | const { 59 | value: { title, description, changes }, 60 | } = data; 61 | return new ParameterChangeProposal( 62 | title, 63 | description, 64 | ParamChanges.fromData(changes) 65 | ); 66 | } 67 | 68 | public toData(): ParameterChangeProposal.Data { 69 | const { title, description, changes } = this; 70 | return { 71 | type: 'params/ParameterChangeProposal', 72 | value: { 73 | title, 74 | description, 75 | changes: ParamChanges.toData(changes), 76 | }, 77 | }; 78 | } 79 | } 80 | 81 | export namespace ParameterChangeProposal { 82 | export interface Data { 83 | type: 'params/ParameterChangeProposal'; 84 | value: { 85 | title: string; 86 | description: string; 87 | changes: ParamChange.Data[]; 88 | }; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/core/staking/msgs/MsgEditValidator.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { Dec, Int } from '../../numeric'; 3 | import { ValAddress } from '../../bech32'; 4 | import { Validator } from '../Validator'; 5 | 6 | /** 7 | * A validator can edit its delegate information, such as moniker, website, commission 8 | * rate, etc. 9 | * 10 | * You must use special or sentinel values to inform that you want to leave the current 11 | * field untouched. For `Description`,` you should start with [[MsgEditValidator.DESC_DO_NOT_MODIFY]] and 12 | * change each field you wish to modify individually. 13 | */ 14 | export class MsgEditValidator extends JSONSerializable { 15 | /** 16 | * @param Description new description to apply 17 | * @param address new address to apply 18 | * @param commission_rate new commission rates to apply 19 | * @param min_self_delegation new min self delegation 20 | */ 21 | constructor( 22 | public description: Validator.Description, 23 | public validator_address: ValAddress, 24 | public commission_rate?: Dec, 25 | public min_self_delegation?: Int 26 | ) { 27 | super(); 28 | } 29 | 30 | public static fromData(data: MsgEditValidator.Data): MsgEditValidator { 31 | const { 32 | value: { 33 | description, 34 | validator_address, 35 | commission_rate, 36 | min_self_delegation, 37 | }, 38 | } = data; 39 | return new MsgEditValidator( 40 | description, 41 | validator_address, 42 | commission_rate ? new Dec(commission_rate) : undefined, 43 | min_self_delegation ? new Int(min_self_delegation) : undefined 44 | ); 45 | } 46 | 47 | public toData(): MsgEditValidator.Data { 48 | const { 49 | description, 50 | validator_address, 51 | commission_rate, 52 | min_self_delegation, 53 | } = this; 54 | return { 55 | type: 'staking/MsgEditValidator', 56 | value: { 57 | description, 58 | validator_address, 59 | commission_rate: commission_rate 60 | ? commission_rate.toString() 61 | : undefined, 62 | min_self_delegation: min_self_delegation 63 | ? min_self_delegation.toString() 64 | : undefined, 65 | }, 66 | }; 67 | } 68 | } 69 | 70 | export namespace MsgEditValidator { 71 | export const DESC_DO_NOT_MODIFY: Validator.Description.Data = { 72 | moniker: '[do-not-modify]', 73 | website: '[do-not-modify]', 74 | identity: '[do-not-modify]', 75 | details: '[do-not-modify]', 76 | security_contact: '[do-not-modify]', 77 | }; 78 | 79 | export interface Data { 80 | type: 'staking/MsgEditValidator'; 81 | value: { 82 | description: any; 83 | validator_address: ValAddress; 84 | commission_rate?: string; 85 | min_self_delegation?: string; 86 | }; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/core/StdSignMsg.ts: -------------------------------------------------------------------------------- 1 | import { StdFee } from './StdFee'; 2 | import { Msg } from './Msg'; 3 | import { JSONSerializable } from '../util/json'; 4 | import { StdTx } from './StdTx'; 5 | 6 | /** 7 | * A sign message is a data structure that is used to create a [[StdSignature]] to be later 8 | * appended to the list of signatures in an [[StdTx]]. Essentially, it contains all the 9 | * information needed to sign and build a transaction, and can be described as an 10 | * "unsigned transaction." 11 | */ 12 | export class StdSignMsg extends JSONSerializable { 13 | /** 14 | * 15 | * @param chain_id ID of blockchain to submit transaction to 16 | * @param account_number account number on blockchain 17 | * @param sequence Sequence number (nonce), number of signed previous transactions by 18 | * account included on the blockchain at time of broadcast. 19 | * @param fee transaction fee 20 | * @param msgs list of messages to include 21 | * @param memo optional note 22 | */ 23 | constructor( 24 | public chain_id: string, 25 | public account_number: number, 26 | public sequence: number, 27 | public fee: StdFee, 28 | public msgs: Msg[], 29 | public memo: string = '', 30 | public timeout_height: number = 0 31 | ) { 32 | super(); 33 | } 34 | 35 | public toData(): StdSignMsg.Data { 36 | const { 37 | chain_id, 38 | account_number, 39 | sequence, 40 | fee, 41 | msgs, 42 | memo, 43 | timeout_height, 44 | } = this; 45 | return { 46 | chain_id, 47 | account_number: account_number.toString(), 48 | sequence: sequence.toString(), 49 | fee: fee.toData(), 50 | msgs: msgs.map(m => m.toData()), 51 | memo, 52 | timeout_height: 53 | timeout_height !== 0 ? timeout_height.toFixed() : undefined, 54 | }; 55 | } 56 | 57 | public static fromData(data: StdSignMsg.Data): StdSignMsg { 58 | const { 59 | chain_id, 60 | account_number, 61 | sequence, 62 | fee, 63 | msgs, 64 | memo, 65 | timeout_height, 66 | } = data; 67 | return new StdSignMsg( 68 | chain_id, 69 | Number.parseInt(account_number) || 0, 70 | Number.parseInt(sequence) || 0, 71 | StdFee.fromData(fee), 72 | msgs.map(m => Msg.fromData(m)), 73 | memo, 74 | Number.parseInt(timeout_height ?? '0') 75 | ); 76 | } 77 | 78 | /** 79 | * You get the [[StdTx]] value from a `StdSignMsg` (without the signature). 80 | */ 81 | public toStdTx(): StdTx { 82 | const { fee, msgs, memo, timeout_height } = this; 83 | return new StdTx(msgs, fee, [], memo, timeout_height); 84 | } 85 | } 86 | 87 | export namespace StdSignMsg { 88 | export interface Data { 89 | chain_id: string; 90 | account_number: string; 91 | sequence: string; 92 | fee: StdFee.Data; 93 | msgs: Msg.Data[]; 94 | memo: string; 95 | timeout_height?: string; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/core/auth/LazyGradedVestingAccount.spec.ts: -------------------------------------------------------------------------------- 1 | import { LazyGradedVestingAccount } from './LazyGradedVestingAccount'; 2 | import { Coins } from '../Coins'; 3 | import { Dec } from '../numeric'; 4 | const data = require('./LazyGradedVestingAccount.data.json'); 5 | 6 | describe('LazyGradedVestingAccount', () => { 7 | it('deserializes correctly', () => { 8 | const acct = LazyGradedVestingAccount.fromData({ 9 | type: 'core/LazyGradedVestingAccount', 10 | value: { 11 | address: 'terra1upg95nlwkfkrq4hhjrn3k9s6ud0aqx36gwnlsn', 12 | coins: [ 13 | { 14 | denom: 'ukrw', 15 | amount: '3952727625434', 16 | }, 17 | { 18 | denom: 'uluna', 19 | amount: '48919046', 20 | }, 21 | { 22 | denom: 'umnt', 23 | amount: '35243811596', 24 | }, 25 | { 26 | denom: 'usdr', 27 | amount: '1212381', 28 | }, 29 | { 30 | denom: 'uusd', 31 | amount: '474532', 32 | }, 33 | ], 34 | public_key: null, 35 | account_number: '684082', 36 | sequence: '0', 37 | original_vesting: [ 38 | { 39 | denom: 'uluna', 40 | amount: '5000000000000', 41 | }, 42 | ], 43 | delegated_free: [], 44 | delegated_vesting: [ 45 | { 46 | denom: 'uluna', 47 | amount: '1338029091449', 48 | }, 49 | ], 50 | end_time: '0', 51 | vesting_schedules: [ 52 | { 53 | denom: 'uluna', 54 | schedules: [ 55 | { 56 | start_time: '1558677600', 57 | end_time: '1561356000', 58 | ratio: '0.100000000000000000', 59 | }, 60 | { 61 | start_time: '1561356000', 62 | end_time: '1587708000', 63 | ratio: '0.270000000000000000', 64 | }, 65 | { 66 | start_time: '1587708000', 67 | end_time: '1600927200', 68 | ratio: '0.480000000000000000', 69 | }, 70 | { 71 | start_time: '1600927200', 72 | end_time: '1603519200', 73 | ratio: '0.150000000000000000', 74 | }, 75 | ], 76 | }, 77 | ], 78 | }, 79 | }); 80 | 81 | expect(acct.vesting_schedules[0].schedules[0]).toMatchObject({ 82 | start_time: 1558677600, 83 | end_time: 1561356000, 84 | ratio: new Dec(0.1), 85 | }); 86 | expect(acct.delegated_free instanceof Coins).toBe(true); 87 | }); 88 | 89 | it('deserializes according to examples', () => { 90 | data.forEach((ex: LazyGradedVestingAccount.Data) => { 91 | const acct = LazyGradedVestingAccount.fromData(ex); 92 | expect(acct.toData()).toMatchObject(ex); 93 | }); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /src/core/staking/msgs/MsgCreateValidator.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../../util/json'; 2 | import { Coin } from '../../Coin'; 3 | import { Int } from '../../numeric'; 4 | import { AccAddress, ValAddress, ValConsPubKey } from '../../bech32'; 5 | import { Validator } from '../Validator'; 6 | 7 | /** 8 | * For new validators, this message registers a validator address to be a delegate on 9 | * the blockchain. 10 | */ 11 | export class MsgCreateValidator extends JSONSerializable { 12 | /** 13 | * 14 | * @param description validator's delegate information 15 | * @param commission validator's commission policy 16 | * @param min_self_delegation minimum self delegation 17 | * @param delegator_address validator's account address 18 | * @param validator_address validator's operator address 19 | * @param pubkey validator's consensus public key 20 | * @param value amount to use for self-delegation 21 | */ 22 | constructor( 23 | public description: Validator.Description, 24 | public commission: Validator.CommissionRates, 25 | public min_self_delegation: Int, 26 | public delegator_address: AccAddress, 27 | public validator_address: ValAddress, 28 | public pubkey: ValConsPubKey.Data, 29 | public value: Coin 30 | ) { 31 | super(); 32 | } 33 | 34 | public static fromData(data: MsgCreateValidator.Data): MsgCreateValidator { 35 | const { 36 | value: { 37 | description, 38 | commission, 39 | min_self_delegation, 40 | delegator_address, 41 | validator_address, 42 | pubkey, 43 | value, 44 | }, 45 | } = data; 46 | return new MsgCreateValidator( 47 | description, 48 | Validator.CommissionRates.fromData(commission), 49 | new Int(min_self_delegation), 50 | delegator_address, 51 | validator_address, 52 | pubkey, 53 | Coin.fromData(value) 54 | ); 55 | } 56 | 57 | public toData(): MsgCreateValidator.Data { 58 | const { 59 | description, 60 | commission, 61 | min_self_delegation, 62 | delegator_address, 63 | validator_address, 64 | pubkey, 65 | value, 66 | } = this; 67 | return { 68 | type: 'staking/MsgCreateValidator', 69 | value: { 70 | description, 71 | commission: commission.toData(), 72 | min_self_delegation: min_self_delegation.toString(), 73 | delegator_address, 74 | validator_address, 75 | pubkey, 76 | value: value.toData(), 77 | }, 78 | }; 79 | } 80 | } 81 | 82 | export namespace MsgCreateValidator { 83 | export interface Data { 84 | type: 'staking/MsgCreateValidator'; 85 | value: { 86 | description: Validator.Description; 87 | commission: Validator.CommissionRates.Data; 88 | min_self_delegation: string; 89 | delegator_address: AccAddress; 90 | validator_address: ValAddress; 91 | pubkey: ValConsPubKey.Data; 92 | value: Coin.Data; 93 | }; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/core/auth/Account.spec.ts: -------------------------------------------------------------------------------- 1 | import { Account } from './Account'; 2 | import { Coins } from '../Coins'; 3 | import { PublicKey } from '../PublicKey'; 4 | 5 | const data = require('./Account.data.json'); 6 | 7 | describe('Account', () => { 8 | it('deserializes accounts correctly', () => { 9 | const data: Account.Data = { 10 | type: 'core/Account', 11 | value: { 12 | address: 'terra12fm3tql2uu0gheuj3st9cwz7ml97tq9mla88c2', 13 | coins: [ 14 | { 15 | denom: 'ukrw', 16 | amount: '1077000000', 17 | }, 18 | { 19 | denom: 'uluna', 20 | amount: '10203920', 21 | }, 22 | ], 23 | public_key: { 24 | type: 'tendermint/PubKeySecp256k1', 25 | value: 'AvBeqhogW0wd7OtF8M8hJ/P1A/IBY1+uNvBO/tbVlfq2', 26 | }, 27 | account_number: '251248', 28 | sequence: '58', 29 | }, 30 | }; 31 | const acct = Account.fromData(data); 32 | expect(acct).toMatchObject({ 33 | address: 'terra12fm3tql2uu0gheuj3st9cwz7ml97tq9mla88c2', 34 | coins: new Coins({ 35 | ukrw: '1077000000', 36 | uluna: '10203920', 37 | }), 38 | public_key: { 39 | value: 'AvBeqhogW0wd7OtF8M8hJ/P1A/IBY1+uNvBO/tbVlfq2', 40 | }, 41 | account_number: 251248, 42 | sequence: 58, 43 | }); 44 | expect(acct.toData()).toMatchObject(data); 45 | }); 46 | 47 | it('deserializes a new account correctly', () => { 48 | // a new account does not yet have a public key 49 | const newAccount: Account.Data = { 50 | type: 'core/Account', 51 | value: { 52 | address: '', 53 | coins: [], 54 | public_key: null, 55 | account_number: '0', 56 | sequence: '0', 57 | }, 58 | }; 59 | 60 | expect(Account.fromData(newAccount).toData()).toMatchObject(newAccount); 61 | }); 62 | 63 | it('serializes accounts correctly', () => { 64 | const acct = new Account( 65 | 'terra12fm3tql2uu0gheuj3st9cwz7ml97tq9mla88c2', 66 | new Coins({ 67 | uluna: 10203920, 68 | }), 69 | new PublicKey('tendermint/PubKeySecp256k1', 'abc'), 70 | 251248, 71 | 58 72 | ); 73 | 74 | expect(acct.toData()).toMatchObject({ 75 | type: 'core/Account', 76 | value: { 77 | address: 'terra12fm3tql2uu0gheuj3st9cwz7ml97tq9mla88c2', 78 | coins: [ 79 | { 80 | denom: 'uluna', 81 | amount: '10203920', 82 | }, 83 | ], 84 | public_key: { 85 | type: 'tendermint/PubKeySecp256k1', 86 | value: 'abc', 87 | }, 88 | account_number: '251248', 89 | sequence: '58', 90 | }, 91 | }); 92 | }); 93 | 94 | it('deserializes from example data', () => { 95 | data.forEach((ex: Account.Data) => { 96 | expect(Account.fromData(ex).toData()).toMatchObject(ex); 97 | }); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /src/core/oracle/params.ts: -------------------------------------------------------------------------------- 1 | import { Denom } from '../Denom'; 2 | import { ParamChange } from '../params/ParamChange'; 3 | import { Convert } from '../../util/convert'; 4 | import { Dec } from '../numeric'; 5 | export type OracleWhitelist = { 6 | name: Denom; 7 | tobin_tax: Dec; 8 | }[]; 9 | 10 | export namespace OracleWhitelist { 11 | export type Data = { 12 | name: Denom; 13 | tobin_tax: string; 14 | }[]; 15 | } 16 | 17 | type VotePeriod = ParamChange.Type<'oracle', 'VotePeriod', number>; 18 | type VoteThreshold = ParamChange.Type<'oracle', 'VoteThreshold', Dec>; 19 | type RewardBand = ParamChange.Type<'oracle', 'RewardBand', Dec>; 20 | type RewardDistributionWindow = ParamChange.Type< 21 | 'oracle', 22 | 'RewardDistributionWindow', 23 | number 24 | >; 25 | 26 | type Whitelist = ParamChange.Type<'oracle', 'Whitelist', OracleWhitelist>; 27 | type SlashFraction = ParamChange.Type<'oracle', 'SlashFraction', Dec>; 28 | type SlashWindow = ParamChange.Type<'oracle', 'SlashWindow', number>; 29 | type MinValidPerWindow = ParamChange.Type<'oracle', 'MinValidPerWindow', Dec>; 30 | 31 | export type OracleParamChange = 32 | | VotePeriod 33 | | VoteThreshold 34 | | RewardBand 35 | | RewardDistributionWindow 36 | | Whitelist 37 | | SlashFraction 38 | | SlashWindow 39 | | MinValidPerWindow; 40 | 41 | export namespace OracleParamChange { 42 | export type Data = 43 | | ParamChange.Data.Type 44 | | ParamChange.Data.Type 45 | | ParamChange.Data.Type 46 | | ParamChange.Data.Type 47 | | ParamChange.Data.Type 48 | | ParamChange.Data.Type 49 | | ParamChange.Data.Type 50 | | ParamChange.Data.Type; 51 | } 52 | 53 | export interface OracleParamChanges { 54 | oracle?: { 55 | VotePeriod?: number; 56 | VoteThreshold?: Dec; 57 | RewardBand?: Dec; 58 | RewardDistributionWindow?: number; 59 | Whitelist?: OracleWhitelist; 60 | SlashFraction?: Dec; 61 | SlashWindow?: number; 62 | MinValidPerWindow?: Dec; 63 | }; 64 | } 65 | 66 | export namespace OracleParamChanges { 67 | export const ConversionTable = { 68 | oracle: { 69 | VotePeriod: [Convert.toNumber, Convert.toFixed], 70 | VoteThreshold: [Convert.toDec, Convert.toString], 71 | RewardBand: [Convert.toDec, Convert.toString], 72 | RewardDistributionWindow: [Convert.toNumber, Convert.toFixed], 73 | Whitelist: [ 74 | (c: OracleWhitelist.Data): OracleWhitelist => 75 | c.map(v => ({ 76 | name: v.name, 77 | tobin_tax: new Dec(v.tobin_tax), 78 | })), 79 | (c: OracleWhitelist): OracleWhitelist.Data => 80 | c.map(v => ({ 81 | name: v.name, 82 | tobin_tax: v.tobin_tax.toString(), 83 | })), 84 | ], 85 | SlashFraction: [Convert.toDec, Convert.toString], 86 | SlashWindow: [Convert.toNumber, Convert.toFixed], 87 | MinValidPerWindow: [Convert.toDec, Convert.toString], 88 | }, 89 | }; 90 | } 91 | -------------------------------------------------------------------------------- /src/core/treasury/PolicyConstraints.ts: -------------------------------------------------------------------------------- 1 | import { JSONSerializable } from '../../util/json'; 2 | import { Coin } from '../Coin'; 3 | import { Dec, Numeric } from '../numeric'; 4 | 5 | /** 6 | * This captures the Treasury module's `tax_policy` and `reward_policy` parameters, which 7 | * determine how the Tax Rate and Reward Weight values are allowed to change. 8 | */ 9 | export class PolicyConstraints extends JSONSerializable { 10 | /** 11 | * Minimum value for rate. 12 | */ 13 | public rate_min: Dec; 14 | 15 | /** 16 | * Maximum value for rate. 17 | */ 18 | public rate_max: Dec; 19 | 20 | /** 21 | * Ratio of current value that rate is allowed to change in one update. 22 | */ 23 | public change_rate_max: Dec; 24 | 25 | /** 26 | * 27 | * @param rate_min minimum value 28 | * @param rate_max maximum value 29 | * @param cap Tax Cap (only applicable for Tax Rate) 30 | * @param change_rate_max max change % 31 | */ 32 | constructor( 33 | rate_min: Numeric.Input, 34 | rate_max: Numeric.Input, 35 | public cap: Coin, 36 | change_rate_max: Numeric.Input 37 | ) { 38 | super(); 39 | this.rate_min = new Dec(rate_min); 40 | this.rate_max = new Dec(rate_max); 41 | this.change_rate_max = new Dec(change_rate_max); 42 | } 43 | 44 | public static fromData(data: PolicyConstraints.Data): PolicyConstraints { 45 | const { rate_min, rate_max, cap, change_rate_max } = data; 46 | return new PolicyConstraints( 47 | rate_min, 48 | rate_max, 49 | Coin.fromData(cap), 50 | change_rate_max 51 | ); 52 | } 53 | 54 | public toData(): PolicyConstraints.Data { 55 | const { rate_min, rate_max, cap, change_rate_max } = this; 56 | return { 57 | rate_min: rate_min.toString(), 58 | rate_max: rate_max.toString(), 59 | cap: cap.toData(), 60 | change_rate_max: change_rate_max.toString(), 61 | }; 62 | } 63 | 64 | /** 65 | * You can simulate the result of the clamping algorithm, which subjects updates in 66 | * rate to the rules defined by the `PolicyConstraints`. 67 | * 68 | * @param prevRate previous rate 69 | * @param newRate next rate 70 | * @returns New rate, after clamping constraints have been applied 71 | */ 72 | public clamp(prevRate: Numeric.Input, newRate: Numeric.Input): Dec { 73 | const p: Dec = new Dec(prevRate); // prev 74 | let n: Dec = new Dec(newRate); // new 75 | 76 | if (n.lt(this.rate_min)) { 77 | n = this.rate_min; 78 | } else if (n.gt(this.rate_max)) { 79 | n = this.rate_max; 80 | } 81 | 82 | const delta: Dec = n.sub(p); 83 | if (n.gt(p)) { 84 | if (delta.gt(this.change_rate_max)) { 85 | n = p.add(this.change_rate_max); 86 | } 87 | } else { 88 | if (delta.abs().gt(this.change_rate_max)) { 89 | n = p.sub(this.change_rate_max); 90 | } 91 | } 92 | return n; 93 | } 94 | } 95 | 96 | export namespace PolicyConstraints { 97 | export interface Data { 98 | rate_min: string; 99 | rate_max: string; 100 | cap: Coin.Data; 101 | change_rate_max: string; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /integration-tests/paramchangeproposal.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MsgSubmitProposal, 3 | isTxError, 4 | LocalTerra, 5 | Dec, 6 | ParameterChangeProposal, 7 | Coin, 8 | Coins, 9 | PolicyConstraints, 10 | Msg, 11 | TxAPI, 12 | StdFee, 13 | } from '../src'; 14 | 15 | // test1 key from localterra accounts 16 | const terra = new LocalTerra(); 17 | const { test1 } = terra.wallets; 18 | 19 | const proposal = new ParameterChangeProposal('testing params', 'yay!', { 20 | distribution: { 21 | communitytax: new Dec(0), 22 | baseproposerreward: new Dec(0.1), 23 | bonusproposerreward: new Dec(0.1), 24 | withdrawaddrenabled: true, 25 | }, 26 | staking: { 27 | UnbondingTime: 33, 28 | MaxValidators: 9999, 29 | KeyMaxEntries: 2323, 30 | BondDenom: 'uluna', 31 | }, 32 | slashing: { 33 | MaxEvidenceAge: 234234, 34 | SignedBlocksWindow: 1, 35 | MinSignedPerWindow: new Dec(1), 36 | DowntimeJailDuration: 1, 37 | SlashFractionDoubleSign: new Dec(100), 38 | SlashFractionDowntime: new Dec(213.123), 39 | }, 40 | treasury: { 41 | TaxPolicy: new PolicyConstraints(0, 100, new Coin('unused', 0), 3), 42 | RewardPolicy: new PolicyConstraints( 43 | 0, 44 | 1023423340, 45 | new Coin('unused', 0), 46 | 3 47 | ), 48 | SeigniorageBurdenTarget: new Dec('2342.234234'), 49 | MiningIncrement: new Dec(23423423423.234234234234982), 50 | WindowShort: 50, 51 | WindowLong: 2, 52 | WindowProbation: 30, 53 | }, 54 | oracle: { 55 | VotePeriod: 345345, 56 | VoteThreshold: new Dec('2342.234333'), 57 | RewardBand: new Dec('234343'), 58 | RewardDistributionWindow: 345345, 59 | SlashFraction: new Dec(23423.232343), 60 | SlashWindow: 343311, 61 | MinValidPerWindow: new Dec(2342.234234), 62 | }, 63 | market: { 64 | PoolRecoveryPeriod: 234234234, 65 | BasePool: new Dec(232323232), 66 | MinStabilitySpread: new Dec(343434), 67 | }, 68 | gov: { 69 | depositparams: { 70 | min_deposit: new Coins({ ukrw: 5, uluna: 2 }), 71 | max_deposit_period: 30434, 72 | }, 73 | votingparams: { 74 | voting_period: 434243234, 75 | }, 76 | tallyparams: { 77 | quorum: new Dec(234234.2334), 78 | threshold: new Dec(23423.2323), 79 | veto_threshold: new Dec(1232.234), 80 | }, 81 | }, 82 | mint: { 83 | InflationRateChange: new Dec(0.01), 84 | BlocksPerYear: 1000000, 85 | MintDenom: 'uluna', 86 | InflationMin: new Dec(0.01), 87 | InflationMax: new Dec(0.01), 88 | GoalBonded: new Dec(0.01), 89 | }, 90 | wasm: { 91 | MaxContractGas: 1000000, 92 | MaxContractMsgSize: 1000000, 93 | MaxContractSize: 1000000, 94 | }, 95 | }); 96 | 97 | async function main(): Promise { 98 | const submitProposal = new MsgSubmitProposal( 99 | proposal, 100 | {}, 101 | test1.key.accAddress 102 | ); 103 | 104 | const tx = await test1.createAndSignTx({ 105 | msgs: [submitProposal], 106 | fee: new StdFee(1000000, { uluna: 1000000000 }), 107 | }); 108 | 109 | const result = await terra.tx.broadcast(tx); 110 | console.log(result); 111 | } 112 | 113 | main().then(console.log); 114 | --------------------------------------------------------------------------------