Last active
April 2, 2022 19:54
-
-
Save dakom/f76f70794044f0c4f454ecddbd53bfd5 to your computer and use it in GitHub Desktop.
Terra helpers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {AccAddress, Coins, Wallet, CreateTxOptions, MsgStoreCode, MsgInstantiateContract, MsgExecuteContract} from "@terra-money/terra.js"; | |
import {TxResult} from '@terra-dev/wallet-types'; | |
// frequency of polling a transaction to see if it's ready | |
export const DEFAULT_TRANSACTION_INFO_POLL_WAIT:number = 500; //500ms | |
// amount of time to wait before giving up on transaction finishing | |
export const DEFAULT_TRANSACTION_INFO_TIMEOUT:number = 1000 * 60 * 5; // 5 mins | |
// amount of time to wait after uploading, due to localterra weirdness | |
// not sure if this is strictly necessary, but w/e | |
export const UPLOAD_DELAY_AFTER:number = 1000; | |
// contract should be supplied via fs.readFile(filepath, 'base64') | |
export function contractUpload(wallet:Wallet, contract:string):Promise<number> { | |
const msg = new MsgStoreCode(wallet.key.accAddress, contract); | |
return postWallet(wallet, { msgs: [msg] }) | |
.then(res => { | |
if(!res.success) { | |
return Promise.reject(res); | |
} else { | |
return requestTxInfo({ | |
wallet, | |
hash: res.result.txhash, | |
validator: (res:any) => { | |
if(res.logs?.length > 0) { | |
const code_ids = res.logs[0].eventsByType?.store_code?.code_id; | |
if(code_ids?.length > 0) { | |
return new Promise(resolve => { | |
setTimeout(() => { | |
resolve(parseInt(code_ids[0])); | |
}, UPLOAD_DELAY_AFTER); | |
}) | |
} | |
} | |
return Promise.reject("bad code id!"); | |
} | |
}) | |
} | |
}) | |
} | |
export function contractInstantiate(wallet:Wallet, codeId: number, payload: any, admin:string = "", init_coins?: Coins.Input):Promise<string> { | |
const msg = new MsgInstantiateContract(wallet.key.accAddress, admin, codeId, payload, init_coins); | |
return postWallet(wallet, { msgs: [msg] }) | |
.then(res => { | |
if(!res.success) { | |
return Promise.reject(res); | |
} else { | |
return requestTxInfo({ | |
wallet, | |
hash: res.result.txhash, | |
validator: (res:any) => { | |
if(res.logs?.length > 0) { | |
const addrs = res.logs[0].eventsByType?.instantiate_contract?.contract_address; | |
if(addrs?.length > 0) { | |
return Promise.resolve(addrs[0]); | |
} | |
} | |
return Promise.reject("bad contract addr!"); | |
} | |
}); | |
} | |
}) | |
} | |
export function contractExecute(wallet:Wallet, contract: AccAddress, payload: any, coins?: Coins.Input):Promise<string> { | |
const msg = new MsgExecuteContract(wallet.key.accAddress, contract, payload, coins); | |
return postWallet(wallet, { msgs: [msg] }) | |
.then(res => { | |
if(!res.success) { | |
return Promise.reject(res); | |
} else { | |
return requestTxInfo({ | |
wallet, | |
hash: res.result.txhash, | |
validator: (res:any) => { | |
//TODO - get binary! https://github.com/terra-money/terra.js/issues/133 | |
if(res.logs?.length > 0) { | |
return Promise.resolve(res.logs[0].eventsByType?.from_contract); | |
} | |
return Promise.resolve(null); | |
} | |
}); | |
} | |
}) | |
} | |
export function contractQuery(wallet:Wallet, addr:string, query:any):Promise<any> { | |
return wallet.lcd.wasm.contractQuery(addr, query) | |
} | |
export function taxRateQuery(wallet: Wallet):Promise<any> { | |
return wallet.lcd.treasury.taxRate() | |
} | |
/// below here is all internal helpers | |
interface TxInfoRequest { | |
timeout?: number, | |
pollWait?: number, | |
//async validation function to sanity check the result | |
validator?: (res:any) => Promise<any>, | |
hash: string, | |
wallet: Wallet | |
} | |
//we need to poll at a regular interval, but also timeout at some point | |
//see discussion at https://github.com/terra-money/wallet-provider/issues/23#issuecomment-918725271 | |
//The validator allows for async verification (failure there will reject properly, not case a re-poll) | |
function requestTxInfo({timeout, pollWait, hash, wallet, validator}:TxInfoRequest):Promise<any> { | |
return new Promise((resolve, reject) => { | |
const DEFAULT_VALIDATOR = (res:any) => { | |
if(res == null) { | |
return Promise.reject("no result!"); | |
} else { | |
return Promise.resolve(res); | |
} | |
} | |
const TIMEOUT = timeout == null ? DEFAULT_TRANSACTION_INFO_TIMEOUT : timeout; | |
const POLL_WAIT = pollWait == null ? DEFAULT_TRANSACTION_INFO_POLL_WAIT : pollWait; | |
const VALIDATOR = validator == null ? DEFAULT_VALIDATOR : validator; | |
let stopProcessing = false; | |
let pollId:NodeJS.Timeout; | |
function clearTimeouts() { | |
stopProcessing = true; | |
clearTimeout(pollId); | |
clearTimeout(timeoutId); | |
} | |
let timeoutId = setTimeout(() => { | |
clearTimeouts(); | |
reject("timeout"); | |
}, TIMEOUT); | |
function poll() { | |
if(!stopProcessing) { | |
wallet.lcd.tx.txInfo(hash) | |
.then(res => { | |
return VALIDATOR(res) | |
.then(data => ({valid: true, data})) | |
.catch(reason => Promise.resolve({valid: false, data: reason})) | |
}) | |
.then( | |
({valid, data}) => { | |
clearTimeouts(); | |
if(!valid) { | |
// validation failed - don't poll again, fully reject | |
reject(data); | |
} else { | |
// validation succeeded - resolve | |
resolve(data); | |
} | |
}, | |
// error in tx itself, i.e. 404, try again! | |
(_err) => { | |
pollId = setTimeout(() => { | |
poll(); | |
}, POLL_WAIT); | |
} | |
) | |
} | |
} | |
poll(); | |
}) | |
} | |
function postWallet(wallet: Wallet, tx: CreateTxOptions):Promise<TxResult> { | |
return wallet | |
.createAndSignTx(tx) | |
.then(tx => wallet.lcd.tx.broadcastSync(tx)) | |
.then(res => { | |
if(!res) { | |
return { | |
success: false, | |
...tx | |
} as TxResult | |
} else { | |
return { | |
result: res, | |
success: true, | |
...tx | |
} | |
} | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment