@polkadot/api#SubmittableResult TypeScript Examples
The following examples show how to use
@polkadot/api#SubmittableResult.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: util.ts From subscan-multisig-react with Apache License 2.0 | 7 votes |
export function handleTxResults(
handler: 'send' | 'signAndSend',
queueSetTxStatus: QueueTxMessageSetStatus,
{ id, txFailedCb = NOOP, txSuccessCb = NOOP, txUpdateCb = NOOP }: QueueTx,
unsubscribe: () => void
): (result: SubmittableResult) => void {
return (result: SubmittableResult): void => {
if (!result || !result.status) {
return;
}
const status = result.status.type.toLowerCase() as QueueTxStatus;
queueSetTxStatus(id, status, result);
txUpdateCb(result);
if (result.status.isFinalized || result.status.isInBlock) {
result.events
.filter(({ event: { section } }) => section === 'system')
.forEach(({ event: { method } }): void => {
if (method === 'ExtrinsicFailed') {
txFailedCb(result);
} else if (method === 'ExtrinsicSuccess') {
txSuccessCb(result);
}
});
} else if (result.isError) {
txFailedCb(result);
}
if (result.isCompleted) {
unsubscribe();
}
};
}
Example #2
Source File: util.ts From crust-apps with Apache License 2.0 | 7 votes |
export function handleTxResults (handler: 'send' | 'signAndSend', queueSetTxStatus: QueueTxMessageSetStatus, { id, txFailedCb = NOOP, txSuccessCb = NOOP, txUpdateCb = NOOP }: QueueTx, unsubscribe: () => void): (result: SubmittableResult) => void {
return (result: SubmittableResult): void => {
if (!result || !result.status) {
return;
}
const status = result.status.type.toLowerCase() as QueueTxStatus;
console.log(`${handler}: status :: ${JSON.stringify(result)}`);
queueSetTxStatus(id, status, result);
txUpdateCb(result);
if (result.status.isFinalized || result.status.isInBlock) {
result.events
.filter(({ event: { section } }) => section === 'system')
.forEach(({ event: { method } }): void => {
if (method === 'ExtrinsicFailed') {
txFailedCb(result);
} else if (method === 'ExtrinsicSuccess') {
txSuccessCb(result);
}
});
} else if (result.isError) {
txFailedCb(result);
}
if (result.isCompleted) {
unsubscribe();
}
};
}
Example #3
Source File: useQueueTx.ts From contracts-ui with GNU General Public License v3.0 | 6 votes |
export function useQueueTx(
extrinsic: SubmittableExtrinsic<'promise'> | null,
accountId: string | null | undefined,
onSuccess: (_: SubmittableResult) => Promise<void>,
onError: VoidFn,
isValid: (_: SubmittableResult) => boolean
): [VoidFn, VoidFn, boolean, boolean] {
const { queue, dismiss, process, txs } = useTransactions();
const [txId, setTxId] = useState<number>(0);
const txIdRef = useRef(txId);
const isProcessing = useMemo(
(): boolean => !!(txs[txId] && txs[txId]?.status === 'processing'),
[txs, txId]
);
const onSubmit = useCallback((): void => {
txId && process(txId);
}, [process, txId]);
const onCancel = useCallback((): void => {
txId && dismiss(txId);
setTxId(0);
}, [dismiss, txId]);
useEffect((): void => {
if (extrinsic && accountId && txId === 0) {
const newId = queue({ extrinsic, accountId, onSuccess, onError, isValid });
setTxId(newId);
txIdRef.current = newId;
}
}, [accountId, extrinsic, isValid, onError, onSuccess, queue, txId]);
return [onSubmit, onCancel, !isNull(txId), isProcessing];
}
Example #4
Source File: ApiSigner.ts From subscan-multisig-react with Apache License 2.0 | 6 votes |
public update(id: number, result: Hash | SubmittableResult): void {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (result instanceof this.#registry.createClass('Hash')) {
// if (result instanceof ClassOf(this.#registry, 'Hash')) {
this.#queueSetTxStatus(id, 'sent', result.toHex());
} else {
this.#queueSetTxStatus(id, (result as SubmittableResult).status.type.toLowerCase() as QueueTxStatus, status);
}
}
Example #5
Source File: utils.ts From evm-provider.js with Apache License 2.0 | 6 votes |
export function handleTxResponse(
result: SubmittableResult,
api: ApiPromise
): Promise<{
result: SubmittableResult;
message?: string;
}> {
return new Promise((resolve, reject) => {
if (result.status.isFinalized || result.status.isInBlock) {
const createdFailed = result.findRecord('evm', 'CreatedFailed');
const executedFailed = result.findRecord('evm', 'ExecutedFailed');
result.events
.filter(({ event: { section } }): boolean => section === 'system')
.forEach((event): void => {
const {
event: { data, method }
} = event;
if (method === 'ExtrinsicFailed') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [dispatchError] = data as any[];
let message = dispatchError.type;
if (dispatchError.isModule) {
try {
const mod = dispatchError.asModule;
const error = api.registry.findMetaError(
new Uint8Array([mod.index.toNumber(), mod.error.toNumber()])
);
message = `${error.section}.${error.name}`;
} catch (error) {
// swallow
}
}
reject({ message, result });
} else if (method === 'ExtrinsicSuccess') {
const failed = createdFailed || executedFailed;
if (failed) {
reject({
message: decodeMessage(
failed.event.data[1].toJSON(),
failed.event.data[2].toJSON() as string
),
result
});
}
resolve({ result });
}
});
} else if (result.isError) {
reject({ result });
}
});
}
Example #6
Source File: Signer.ts From evm-provider.js with Apache License 2.0 | 6 votes |
/**
* Claims a default EVM address for this signer's substrate address
*/
async claimDefaultAccount(): Promise<void> {
const extrinsic = this.provider.api.tx.evmAccounts.claimDefaultAccount();
await extrinsic.signAsync(this._substrateAddress);
await new Promise<void>((resolve, reject) => {
extrinsic
.send((result: SubmittableResult) => {
handleTxResponse(result, this.provider.api)
.then(() => {
resolve();
})
.catch(({ message, result }) => {
if (message === 'evmAccounts.AccountIdHasMapped') {
resolve();
}
reject(message);
});
})
.catch((error) => {
reject(error && error.message);
});
});
}
Example #7
Source File: Signer.ts From evm-provider.js with Apache License 2.0 | 6 votes |
async claimEvmAccount(evmAddress: string): Promise<void> {
const isConnented = await this.isClaimed(evmAddress);
if (isConnented) return;
const publicKey = decodeAddress(this._substrateAddress);
const data = 'Reef evm:' + Buffer.from(publicKey).toString('hex');
const signature = await this._signMessage(evmAddress, data);
const extrinsic = this.provider.api.tx.evmAccounts.claimAccount(
evmAddress,
signature
);
await extrinsic.signAsync(this._substrateAddress);
await new Promise<void>((resolve, reject) => {
extrinsic
.send((result: SubmittableResult) => {
handleTxResponse(result, this.provider.api)
.then(() => {
resolve();
})
.catch(({ message, result }) => {
if (message === 'evmAccounts.AccountIdHasMapped') {
resolve();
}
reject(message);
});
})
.catch((error) => {
reject(error && error.message);
});
});
}
Example #8
Source File: keyring.ts From sdk with Apache License 2.0 | 6 votes |
/**
* send tx with signed data from QR
*/
function addSignatureAndSend(api: ApiPromise, address: string, signed: string) {
return new Promise((resolve) => {
const { tx, payload } = getSubmittable();
if (!!tx.addSignature) {
tx.addSignature(address, `0x${signed}`, payload);
let unsub = () => {};
const onStatusChange = (result: SubmittableResult) => {
if (result.status.isInBlock || result.status.isFinalized) {
const { success, error } = _extractEvents(api, result);
if (success) {
resolve({ hash: tx.hash.toString(), blockHash: (result.status.asInBlock || result.status.asFinalized).toHex() });
}
if (error) {
resolve({ error });
}
unsub();
} else {
(<any>window).send("txStatusChange", result.status.type);
}
};
tx.send(onStatusChange)
.then((res) => {
unsub = res;
})
.catch((err) => {
resolve({ error: err.message });
});
} else {
resolve({ error: "invalid tx" });
}
});
}
Example #9
Source File: Signer.ts From bodhi.js with Apache License 2.0 | 6 votes |
async claimEvmAccount(evmAddress: string): Promise<void> {
const isConnented = await this.isClaimed(evmAddress);
if (isConnented) return;
const publicKey = decodeAddress(this._substrateAddress);
const data = 'acala evm:' + Buffer.from(publicKey).toString('hex');
const signature = await this._signMessage(evmAddress, data);
const extrinsic = this.provider.api.tx.evmAccounts.claimAccount(evmAddress, signature);
await extrinsic.signAsync(this._substrateAddress);
await new Promise<void>((resolve, reject) => {
extrinsic
.send((result: SubmittableResult) => {
handleTxResponse(result, this.provider.api)
.then(() => {
resolve();
})
.catch((err) => {
if (err.message === 'evmAccounts.AccountIdHasMapped') {
resolve();
}
reject(err);
});
})
.catch(reject);
});
}
Example #10
Source File: common.ts From subsocial-js with GNU General Public License v3.0 | 6 votes |
export function getNewIdsFromEvent (txResult: SubmittableResult, eventType: ResultEventType = 'Created'): BN[] {
const newIds: BN[] = []
txResult.events.find(event => {
const { event: { data, method } } = event
if (method.indexOf(eventType) >= 0) {
const [ /* owner */, ...ids ] = data.toArray()
newIds.push(...ids as unknown as BN[])
return true
}
return false
})
return newIds
}
Example #11
Source File: sendTx.ts From bodhi.js with Apache License 2.0 | 6 votes |
sendTx = (api: ApiPromise, extrinsic: SubmittableExtrinsic<'promise'>): Promise<SubmittableResult> => {
return new Promise((resolve, reject) => {
extrinsic
.send((result) => {
handleTxResponse(result, api)
.then(({ result }) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
})
.catch((error) => {
reject(error);
});
});
}
Example #12
Source File: Signer.ts From bodhi.js with Apache License 2.0 | 6 votes |
/**
* Claims a default EVM address for this signer's substrate address
*/
async claimDefaultAccount(): Promise<void> {
const extrinsic = this.provider.api.tx.evmAccounts.claimDefaultAccount();
await extrinsic.signAsync(this._substrateAddress);
await new Promise<void>((resolve, reject) => {
extrinsic
.send((result: SubmittableResult) => {
handleTxResponse(result, this.provider.api)
.then(() => {
resolve();
})
.catch((err) => {
if (err.message === 'evmAccounts.AccountIdHasMapped') {
resolve();
}
reject(err);
});
})
.catch((error) => {
reject(error);
});
});
}
Example #13
Source File: ApiSigner.ts From crust-apps with Apache License 2.0 | 5 votes |
public update (id: number, result: Hash | SubmittableResult): void {
if (result instanceof ClassOf(this.#registry, 'Hash')) {
this.#queueSetTxStatus(id, 'sent', result.toHex());
} else {
this.#queueSetTxStatus(id, result.status.type.toLowerCase() as QueueTxStatus, status);
}
}
Example #14
Source File: keyring.ts From sdk with Apache License 2.0 | 5 votes |
function _extractEvents(api: ApiPromise, result: SubmittableResult) {
if (!result || !result.events) {
return {};
}
let success = false;
let error: DispatchError["type"] = "";
result.events
.filter((event) => !!event.event)
.map(({ event: { data, method, section } }) => {
if (section === "system" && method === "ExtrinsicFailed") {
const [dispatchError] = (data as unknown) as ITuple<[DispatchError]>;
let message = dispatchError.type;
if (dispatchError.isModule) {
try {
const mod = dispatchError.asModule;
const err = api.registry.findMetaError(new Uint8Array([mod.index.toNumber(), mod.error.toNumber()]));
message = `${err.section}.${err.name}`;
} catch (error) {
// swallow error
}
}
(<any>window).send("txUpdateEvent", {
title: `${section}.${method}`,
message,
});
error = message;
} else {
(<any>window).send("txUpdateEvent", {
title: `${section}.${method}`,
message: "ok",
});
if (section == "system" && method == "ExtrinsicSuccess") {
success = true;
}
}
});
return { success, error };
}
Example #15
Source File: keyring.ts From sdk with Apache License 2.0 | 5 votes |
/**
* sign and send extrinsic to network and wait for result.
*/
function sendTx(api: ApiPromise, txInfo: any, paramList: any[], password: string, msgId: string) {
return new Promise(async (resolve) => {
let tx: SubmittableExtrinsic<"promise">;
// wrap tx with council.propose for treasury propose
if (txInfo.txName == "treasury.approveProposal") {
tx = await gov.makeTreasuryProposalSubmission(api, paramList[0], false);
} else if (txInfo.txName == "treasury.rejectProposal") {
tx = await gov.makeTreasuryProposalSubmission(api, paramList[0], true);
} else {
tx = api.tx[txInfo.module][txInfo.call](...paramList);
}
let unsub = () => {};
const onStatusChange = (result: SubmittableResult) => {
if (result.status.isInBlock || result.status.isFinalized) {
const { success, error } = _extractEvents(api, result);
if (success) {
resolve({ hash: tx.hash.toString(), blockHash: (result.status.asInBlock || result.status.asFinalized).toHex() });
}
if (error) {
resolve({ error });
}
unsub();
} else {
(<any>window).send(msgId, result.status.type);
}
};
if (txInfo.isUnsigned) {
tx.send(onStatusChange)
.then((res) => {
unsub = res;
})
.catch((err) => {
resolve({ error: err.message });
});
return;
}
let keyPair: KeyringPair;
if (!txInfo.proxy) {
keyPair = keyring.getPair(hexToU8a(txInfo.sender.pubKey));
} else {
// wrap tx with recovery.asRecovered for proxy tx
tx = api.tx.recovery.asRecovered(txInfo.sender.address, tx);
keyPair = keyring.getPair(hexToU8a(txInfo.proxy.pubKey));
}
try {
keyPair.decodePkcs8(password);
} catch (err) {
resolve({ error: "password check failed" });
}
tx.signAndSend(keyPair, { tip: new BN(txInfo.tip, 10) }, onStatusChange)
.then((res) => {
unsub = res;
})
.catch((err) => {
resolve({ error: err.message });
});
});
}
Example #16
Source File: Queue.tsx From crust-apps with Apache License 2.0 | 5 votes |
function extractEvents (result?: SubmittableResult): ActionStatus[] {
return mergeStatus(
((result && result.events) || [])
// filter events handled globally, or those we are not interested in, these are
// handled by the global overview, so don't add them here
.filter((record): boolean => !!record.event && record.event.section !== 'democracy')
.map(({ event: { data, method, section } }): ActionStatusPartial => {
if (section === 'system' && method === 'ExtrinsicFailed') {
const [dispatchError] = data as unknown as ITuple<[DispatchError]>;
let message = dispatchError.type;
if (dispatchError.isModule) {
try {
const mod = dispatchError.asModule;
const error = dispatchError.registry.findMetaError(mod);
message = `${error.section}.${error.name}`;
} catch (error) {
// swallow
}
}
return {
action: `${section}.${method}`,
message,
status: 'error'
};
} else if (section === 'contracts') {
if (method === 'ContractExecution' && data.length === 2) {
// see if we have info for this contract
const [accountId, encoded] = data;
try {
const abi = getContractAbi(accountId.toString());
if (abi) {
const decoded = abi.decodeEvent(encoded as Bytes);
return {
action: decoded.event.identifier,
message: 'contract event',
status: 'event'
};
}
} catch (error) {
// ABI mismatch?
console.error(error);
}
} else if (method === 'Evicted') {
return {
action: `${section}.${method}`,
message: 'contract evicted',
status: 'error'
};
}
}
return {
action: `${section}.${method}`,
message: EVENT_MESSAGE,
status: 'event'
};
})
);
}
Example #17
Source File: handleTxResponse.ts From bodhi.js with Apache License 2.0 | 5 votes |
export function handleTxResponse(
result: SubmittableResult,
api: ApiPromise
): Promise<{
result: SubmittableResult;
message?: string;
}> {
return new Promise((resolve, reject) => {
if (result.status.isFinalized || result.status.isInBlock) {
const createdFailed = result.findRecord('evm', 'CreatedFailed');
const executedFailed = result.findRecord('evm', 'ExecutedFailed');
result.events
.filter(({ event: { section } }): boolean => section === 'system')
.forEach((event): void => {
const {
event: { data, method }
} = event;
if (method === 'ExtrinsicFailed') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [dispatchError] = data as any[];
let message = dispatchError.type;
if (dispatchError.isModule) {
try {
const mod = dispatchError.asModule;
const error = api.registry.findMetaError(new Uint8Array([mod.index.toNumber(), mod.error.toNumber()]));
message = `${error.section}.${error.name}: ${error.docs}`;
} catch (error) {
// swallow
}
}
reject(makeError(message, { result }));
} else if (method === 'ExtrinsicSuccess') {
const failed = createdFailed || executedFailed;
if (failed) {
reject(
makeError(decodeMessage(failed.event.data[2].toJSON(), failed.event.data[3].toJSON() as string), {
result
})
);
}
resolve({ result });
}
});
} else if (result.isError) {
reject({ result });
}
});
}
Example #18
Source File: Queue.tsx From subscan-multisig-react with Apache License 2.0 | 5 votes |
function extractEvents(result?: SubmittableResult): ActionStatus[] {
return mergeStatus(
((result && result.events) || [])
// filter events handled globally, or those we are not interested in, these are
// handled by the global overview, so don't add them here
.filter((record): boolean => !!record.event && record.event.section !== 'democracy')
// eslint-disable-next-line complexity
.map(({ event: { data, method, section } }): ActionStatusPartial => {
if (section === 'system' && method === 'ExtrinsicFailed') {
const [dispatchError] = data as unknown as ITuple<[DispatchError]>;
let message: string = dispatchError.type;
if (dispatchError.isModule) {
try {
const mod = dispatchError.asModule;
const error = dispatchError.registry.findMetaError(mod);
message = `${error.section}.${error.name}`;
} catch (error) {
// swallow
}
} else if (dispatchError.isToken) {
message = `${dispatchError.type}.${dispatchError.asToken.type}`;
}
return {
action: `${section}.${method}`,
message,
status: 'error',
};
} else if (section === 'contracts') {
// eslint-disable-next-line no-magic-numbers
if (method === 'ContractExecution' && data.length === 2) {
// see if we have info for this contract
const [accountId, encoded] = data;
try {
const abi = getContractAbi(accountId.toString());
if (abi) {
const decoded = abi.decodeEvent(encoded as Bytes);
return {
action: decoded.event.identifier,
message: 'contract event',
status: 'event',
};
}
} catch (error) {
// ABI mismatch?
console.error(error);
}
} else if (method === 'Evicted') {
return {
action: `${section}.${method}`,
message: 'contract evicted',
status: 'error',
};
}
}
return {
action: `${section}.${method}`,
message: EVENT_MESSAGE,
status: 'event',
};
})
);
}
Example #19
Source File: shared.ts From commonwealth with GNU General Public License v3.0 | 4 votes |
public createTXModalData(
author: SubstrateAccount,
txFunc: (api: ApiPromise) => SubmittableExtrinsic<'promise'>,
txName: string,
objName: string,
cb?: (success: boolean) => void, // TODO: remove this argument
): ITXModalData {
// TODO: check if author has funds for tx fee
const events = new EventEmitter();
return {
author,
txType: txName,
cb,
txData: {
events,
unsignedData: async (): Promise<ISubstrateTXData> => {
const txHex = txFunc(this.api).method.toHex();
const nonce = this.api.query.system.accountNonce
? await this.api.query.system.accountNonce(author.address)
: (await this.api.query.system.account(author.address)).nonce;
const genesisHash = this.api.genesisHash.toHex();
return {
call: txHex,
nonce: (+nonce).toString(),
blockHash: genesisHash,
isEd25519: author.isEd25519,
};
},
transact: (hexTxOrAddress?: string, signer?: Signer): void => {
let unsubscribe: Promise<VoidFn>;
const txResultHandler = (result: SubmittableResult) => {
const status = result.status;
if (status.isReady) {
console.log(`Pending ${txName}: "${objName}"`);
events.emit(TransactionStatus.Ready.toString(), {});
} else if (status.isFinalized || status.isInBlock) {
for (const e of result.events) {
if (this.api.events.system.ExtrinsicSuccess.is(e.event)) {
notifySuccess(`Confirmed ${txName}`);
events.emit(TransactionStatus.Success.toString(), {
hash: status.isFinalized ? status.asFinalized.toHex() : status.asInBlock.toHex(),
blocknum: this.app.chain.block.height,
timestamp: this.app.chain.block.lastTime,
});
if (unsubscribe) unsubscribe.then((u) => u());
} else if (this.api.events.system.ExtrinsicFailed.is(e.event)) {
const errorData = e.event.data[0] as unknown as DispatchError;
let errorInfo;
if (errorData.isModule) {
const decoded = this.registry.findMetaError(errorData.asModule);
const { docs, method, section } = decoded;
errorInfo = `${section}.${method}: ${docs.join(' ')}`;
} else if (errorData.isBadOrigin) {
errorInfo = 'TX Error: invalid sender origin';
} else if (errorData.isCannotLookup) {
errorInfo = 'TX Error: cannot lookup call';
} else {
errorInfo = 'TX Error: unknown';
}
console.error(errorInfo);
notifyError(`Failed ${txName}: "${objName}"`);
events.emit(TransactionStatus.Failed.toString(), {
hash: status.isFinalized ? status.asFinalized.toHex() : status.asInBlock.toHex(),
blocknum: this.app.chain.block.height,
timestamp: this.app.chain.block.lastTime,
err: errorInfo,
});
if (unsubscribe) unsubscribe.then((u) => u());
}
}
}
};
try {
if (signer) {
this.api.setSigner(signer);
unsubscribe = txFunc(this.api).signAndSend(hexTxOrAddress, txResultHandler);
} else if (hexTxOrAddress) {
unsubscribe = this.api.tx(hexTxOrAddress).send(txResultHandler);
} else {
throw new Error('no signer found');
}
} catch (err) {
if (err.message.indexOf('1014: Priority is too low') !== -1) {
notifyError('Another transaction is already queued for processing');
} else {
notifyError(err.toString());
}
m.redraw();
events.emit(TransactionStatus.Error.toString(), { err: err.toString() });
}
},
}
};
}
Example #20
Source File: TxButton.tsx From crust-apps with Apache License 2.0 | 4 votes |
function TxButton ({ accountId, className = '', extrinsic: propsExtrinsic, icon, isBasic, isBusy, isDisabled, isIcon, isToplevel, isUnsigned, label, onClick, onFailed, onSendRef, onStart, onSuccess, onUpdate, params, tooltip, tx, withSpinner, withoutLink }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const mountedRef = useIsMountedRef();
const { queueExtrinsic } = useContext(StatusContext);
const [isSending, setIsSending] = useState(false);
const [isStarted, setIsStarted] = useState(false);
useEffect((): void => {
(isStarted && onStart) && onStart();
}, [isStarted, onStart]);
const _onFailed = useCallback(
(result: SubmittableResult | null): void => {
mountedRef.current && setIsSending(false);
onFailed && onFailed(result);
},
[onFailed, setIsSending, mountedRef]
);
const _onSuccess = useCallback(
(result: SubmittableResult): void => {
mountedRef.current && setIsSending(false);
onSuccess && onSuccess(result);
},
[onSuccess, setIsSending, mountedRef]
);
const _onStart = useCallback(
(): void => {
mountedRef.current && setIsStarted(true);
},
[setIsStarted, mountedRef]
);
const _onSend = useCallback(
(): void => {
let extrinsics: SubmittableExtrinsic<'promise'>[] | undefined;
if (propsExtrinsic) {
extrinsics = Array.isArray(propsExtrinsic)
? propsExtrinsic
: [propsExtrinsic];
} else if (tx) {
extrinsics = [
tx(...(
isFunction(params)
? params()
: (params || [])
))
];
}
assert(extrinsics?.length, 'Expected generated extrinsic passed to TxButton');
mountedRef.current && withSpinner && setIsSending(true);
extrinsics.forEach((extrinsic): void => {
queueExtrinsic({
accountId: accountId && accountId.toString(),
extrinsic,
isUnsigned,
txFailedCb: withSpinner ? _onFailed : onFailed,
txStartCb: _onStart,
txSuccessCb: withSpinner ? _onSuccess : onSuccess,
txUpdateCb: onUpdate
});
});
onClick && onClick();
},
[_onFailed, _onStart, _onSuccess, accountId, isUnsigned, onClick, onFailed, onSuccess, onUpdate, params, propsExtrinsic, queueExtrinsic, setIsSending, tx, withSpinner, mountedRef]
);
if (onSendRef) {
onSendRef.current = _onSend;
}
return (
<Button
className={className}
icon={icon || 'check'}
isBasic={isBasic}
isBusy={isBusy}
isDisabled={isSending || isDisabled || (!isUnsigned && !accountId) || (
tx
? false
: Array.isArray(propsExtrinsic)
? propsExtrinsic.length === 0
: !propsExtrinsic
)}
isIcon={isIcon}
isToplevel={isToplevel}
label={label || (isIcon ? '' : t<string>('Submit'))}
onClick={_onSend}
tooltip={tooltip}
withoutLink={withoutLink}
/>
);
}
Example #21
Source File: Queue.tsx From crust-apps with Apache License 2.0 | 4 votes |
function Queue ({ children }: Props): React.ReactElement<Props> {
const [stqueue, _setStQueue] = useState<QueueStatus[]>([]);
const [txqueue, _setTxQueue] = useState<QueueTx[]>([]);
const stRef = useRef(stqueue);
const txRef = useRef(txqueue);
const setStQueue = useCallback(
(st: QueueStatus[]): void => {
stRef.current = st;
_setStQueue(st);
},
[]
);
const setTxQueue = useCallback(
(tx: QueueTx[]): void => {
txRef.current = tx;
_setTxQueue(tx);
},
[]
);
const addToTxQueue = useCallback(
(value: QueueTxExtrinsic | QueueTxRpc | QueueTx): void => {
const id = ++nextId;
const removeItem = () => setTxQueue([
...txRef.current.map((item): QueueTx =>
item.id === id
? { ...item, status: 'completed' }
: item
)
]);
setTxQueue([...txRef.current, {
...value,
id,
removeItem,
rpc: (value as QueueTxRpc).rpc || SUBMIT_RPC,
status: 'queued'
}]);
},
[setTxQueue]
);
const queueAction = useCallback(
(_status: ActionStatus | ActionStatus[]): void => {
const status = Array.isArray(_status) ? _status : [_status];
status.length && setStQueue([
...stRef.current,
...(status.map((item): QueueStatus => {
const id = ++nextId;
const removeItem = (): void =>
setStQueue([...stRef.current.filter((item): boolean => item.id !== id)]);
setTimeout(removeItem, REMOVE_TIMEOUT);
return {
...item,
id,
isCompleted: false,
removeItem
};
}))
]);
},
[setStQueue]
);
const queueExtrinsic = useCallback(
(value: PartialQueueTxExtrinsic) => addToTxQueue({ ...value }),
[addToTxQueue]
);
const queuePayload = useCallback(
(registry: Registry, payload: SignerPayloadJSON, signerCb: SignerCallback): void => {
addToTxQueue({
accountId: payload.address,
// this is not great, but the Extrinsic doesn't need a submittable
extrinsic: registry.createType('Extrinsic',
{ method: registry.createType('Call', payload.method) },
{ version: payload.version }
) as unknown as SubmittableExtrinsic,
payload,
signerCb
});
},
[addToTxQueue]
);
const queueRpc = useCallback(
(value: PartialQueueTxRpc) => addToTxQueue({ ...value }),
[addToTxQueue]
);
const queueSetTxStatus = useCallback(
(id: number, status: QueueTxStatus, result?: SubmittableResult, error?: Error): void => {
setTxQueue([
...txRef.current.map((item): QueueTx =>
item.id === id
? {
...item,
error: error === undefined
? item.error
: error,
result: result === undefined
? item.result as SubmittableResult
: result,
status: item.status === 'completed'
? item.status
: status
}
: item
)
]);
queueAction(extractEvents(result));
if (STATUS_COMPLETE.includes(status)) {
setTimeout((): void => {
const item = txRef.current.find((item): boolean => item.id === id);
item && item.removeItem();
}, REMOVE_TIMEOUT);
}
},
[queueAction, setTxQueue]
);
return (
<QueueProvider value={{
queueAction,
queueExtrinsic,
queuePayload,
queueRpc,
queueSetTxStatus,
stqueue,
txqueue
}}>
{children}
</QueueProvider>
);
}
Example #22
Source File: Signer.ts From evm-provider.js with Apache License 2.0 | 4 votes |
/**
*
* @param transaction
* @returns A promise that resolves to the transaction's response
*/
async sendTransaction(
_transaction: Deferrable<TransactionRequest>
): Promise<TransactionResponse> {
this._checkProvider('sendTransaction');
const signerAddress = await this.getSubstrateAddress();
const evmAddress = await this.getAddress();
// estimateResources requires the from parameter.
// However, when creating the contract, there is no from parameter in the tx
const transaction = {
from: evmAddress,
..._transaction
};
const resources = await this.provider.estimateResources(transaction);
// Multiply by 3.1
const gasLimit: BigNumber = resources.gas.mul(31).div(10);
let storageLimit: BigNumber;
// If the storage limit is supplied, override it from the estimateResources
if (transaction.customData) {
if ('storageLimit' in transaction.customData) {
storageLimit = transaction.customData.storageLimit;
if (isNumber(storageLimit)) {
storageLimit = BigNumber.from(storageLimit);
}
}
} else {
storageLimit = resources.storage.mul(31).div(10);
}
let totalLimit = await transaction.gasLimit;
if (totalLimit === null || totalLimit === undefined) {
totalLimit = gasLimit.add(storageLimit);
}
transaction.gasLimit = totalLimit;
const tx = await this.populateTransaction(transaction);
const data = tx.data;
const from = tx.from;
if (!data) {
return logger.throwError('Request data not found');
}
if (!from) {
return logger.throwError('Request from not found');
}
let extrinsic: SubmittableExtrinsic<'promise'>;
// @TODO create contract
if (!tx.to) {
extrinsic = this.provider.api.tx.evm.create(
tx.data,
toBN(tx.value),
toBN(gasLimit),
toBN(storageLimit.isNegative() ? 0 : storageLimit)
);
} else {
extrinsic = this.provider.api.tx.evm.call(
tx.to,
tx.data,
toBN(tx.value),
toBN(gasLimit),
toBN(storageLimit.isNegative() ? 0 : storageLimit)
);
}
await extrinsic.signAsync(signerAddress);
return new Promise((resolve, reject) => {
extrinsic
.send((result: SubmittableResult) => {
handleTxResponse(result, this.provider.api)
.then(() => {
resolve({
hash: extrinsic.hash.toHex(),
from: from || '',
confirmations: 0,
nonce: toBN(tx.nonce).toNumber(),
gasLimit: BigNumber.from(tx.gasLimit || '0'),
gasPrice: BigNumber.from(0),
data: dataToString(data),
value: BigNumber.from(tx.value || '0'),
chainId: 13939,
wait: (confirmations?: number): Promise<TransactionReceipt> => {
return this.provider._resolveTransactionReceipt(
extrinsic.hash.toHex(),
result.status.asInBlock.toHex(),
from
);
}
});
})
.catch(({ message, result }) => {
reject(message);
});
})
.catch((error) => {
reject(error && error.message);
});
});
}
Example #23
Source File: Queue.tsx From subscan-multisig-react with Apache License 2.0 | 4 votes |
function Queue({ children }: Props): React.ReactElement<Props> {
const [stqueue, _setStQueue] = useState<QueueStatus[]>([]);
const [txqueue, _setTxQueue] = useState<QueueTx[]>([]);
const stRef = useRef(stqueue);
const txRef = useRef(txqueue);
const setStQueue = useCallback((st: QueueStatus[]): void => {
stRef.current = st;
_setStQueue(st);
}, []);
const setTxQueue = useCallback((tx: QueueTx[]): void => {
txRef.current = tx;
_setTxQueue(tx);
}, []);
const addToTxQueue = useCallback(
(value: QueueTxExtrinsic | QueueTxRpc | QueueTx): void => {
const id = ++nextId;
const removeItem = () =>
setTxQueue([
...txRef.current.map((item): QueueTx => (item.id === id ? { ...item, status: 'completed' } : item)),
]);
setTxQueue([
...txRef.current,
{
...value,
id,
removeItem,
rpc: (value as QueueTxRpc).rpc || SUBMIT_RPC,
status: 'queued',
},
]);
},
[setTxQueue]
);
const queueAction = useCallback(
(_status: ActionStatus | ActionStatus[]): void => {
const status = Array.isArray(_status) ? _status : [_status];
// eslint-disable-next-line
status.length &&
setStQueue([
...stRef.current,
...status.map((item): QueueStatus => {
const id = ++nextId;
const removeItem = (): void => setStQueue([...stRef.current.filter((item): boolean => item.id !== id)]);
setTimeout(removeItem, REMOVE_TIMEOUT);
return {
...item,
id,
isCompleted: false,
removeItem,
};
}),
]);
},
[setStQueue]
);
const queueExtrinsic = useCallback((value: PartialQueueTxExtrinsic) => addToTxQueue({ ...value }), [addToTxQueue]);
const queuePayload = useCallback(
(registry: Registry, payload: SignerPayloadJSON, signerCb: SignerCallback): void => {
addToTxQueue({
accountId: payload.address,
// this is not great, but the Extrinsic doesn't need a submittable
extrinsic: registry.createType(
'Extrinsic',
{ method: registry.createType('Call', payload.method) },
{ version: payload.version }
) as unknown as SubmittableExtrinsic,
payload,
signerCb,
});
},
[addToTxQueue]
);
const queueRpc = useCallback((value: PartialQueueTxRpc) => addToTxQueue({ ...value }), [addToTxQueue]);
const queueSetTxStatus = useCallback(
(id: number, status: QueueTxStatus, result?: SubmittableResult, error?: Error): void => {
setTxQueue([
...txRef.current.map(
(item): QueueTx =>
item.id === id
? {
...item,
error: error === undefined ? item.error : error,
result: result === undefined ? (item.result as SubmittableResult) : result,
status: item.status === 'completed' ? item.status : status,
}
: item
),
]);
queueAction(extractEvents(result));
if (STATUS_COMPLETE.includes(status)) {
setTimeout((): void => {
const item = txRef.current.find((item): boolean => item.id === id);
// eslint-disable-next-line
item && item.removeItem();
}, REMOVE_TIMEOUT);
}
},
[queueAction, setTxQueue]
);
return (
<QueueProvider
value={{
queueAction,
queueExtrinsic,
queuePayload,
queueRpc,
queueSetTxStatus,
stqueue,
txqueue,
}}
>
{children}
</QueueProvider>
);
}
Example #24
Source File: TxButton.tsx From subscan-multisig-react with Apache License 2.0 | 4 votes |
function TxButton({
accountId,
className = '',
extrinsic: propsExtrinsic,
icon,
isBasic,
isBusy,
isDisabled,
isIcon,
isToplevel,
isUnsigned,
label,
onClick,
onFailed,
onSendRef,
onStart,
onSuccess,
onUpdate,
params,
tooltip,
tx,
withSpinner,
withoutLink,
}: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const mountedRef = useIsMountedRef();
const { queueExtrinsic } = useContext(StatusContext);
const [isSending, setIsSending] = useState(false);
const [isStarted, setIsStarted] = useState(false);
useEffect((): void => {
// eslint-disable-next-line
isStarted && onStart && onStart();
}, [isStarted, onStart]);
const _onFailed = useCallback(
(result: SubmittableResult | null): void => {
// eslint-disable-next-line
mountedRef.current && setIsSending(false);
// eslint-disable-next-line
onFailed && onFailed(result);
},
[onFailed, setIsSending, mountedRef]
);
const _onSuccess = useCallback(
(result: SubmittableResult): void => {
// eslint-disable-next-line
mountedRef.current && setIsSending(false);
// eslint-disable-next-line
onSuccess && onSuccess(result);
},
[onSuccess, setIsSending, mountedRef]
);
const _onStart = useCallback((): void => {
// eslint-disable-next-line
mountedRef.current && setIsStarted(true);
}, [setIsStarted, mountedRef]);
const _onSend = useCallback((): void => {
let extrinsics: SubmittableExtrinsic<'promise'>[] | undefined;
if (propsExtrinsic) {
extrinsics = Array.isArray(propsExtrinsic) ? propsExtrinsic : [propsExtrinsic];
} else if (tx) {
extrinsics = [tx(...(isFunction(params) ? params() : params || []))];
}
assert(extrinsics?.length, 'Expected generated extrinsic passed to TxButton');
// eslint-disable-next-line
mountedRef.current && withSpinner && setIsSending(true);
extrinsics.forEach((extrinsic): void => {
queueExtrinsic({
accountId: accountId && accountId.toString(),
extrinsic,
isUnsigned,
txFailedCb: withSpinner ? _onFailed : onFailed,
txStartCb: _onStart,
txSuccessCb: withSpinner ? _onSuccess : onSuccess,
txUpdateCb: onUpdate,
});
});
// eslint-disable-next-line
onClick && onClick();
}, [
_onFailed,
_onStart,
_onSuccess,
accountId,
isUnsigned,
onClick,
onFailed,
onSuccess,
onUpdate,
params,
propsExtrinsic,
queueExtrinsic,
setIsSending,
tx,
withSpinner,
mountedRef,
]);
if (onSendRef) {
onSendRef.current = _onSend;
}
return (
<Button
className={className}
icon={icon || 'check'}
isBasic={isBasic}
isBusy={isBusy}
isDisabled={
isSending ||
isDisabled ||
(!isUnsigned && !accountId) ||
(tx ? false : Array.isArray(propsExtrinsic) ? propsExtrinsic.length === 0 : !propsExtrinsic)
}
isIcon={isIcon}
isToplevel={isToplevel}
label={label || (isIcon ? '' : t<string>('Submit'))}
onClick={_onSend}
tooltip={tooltip}
withoutLink={withoutLink}
/>
);
}
Example #25
Source File: Signer.ts From bodhi.js with Apache License 2.0 | 4 votes |
/**
*
* @param transaction
* @returns A promise that resolves to the transaction's response
*/
async sendTransaction(_transaction: Deferrable<TransactionRequest>): Promise<TransactionResponse> {
this._checkProvider('sendTransaction');
const signerAddress = await this.getSubstrateAddress();
const evmAddress = await this.getAddress();
// estimateResources requires the from parameter.
// However, when creating the contract, there is no from parameter in the tx
const transaction = {
from: evmAddress,
..._transaction
};
const resources = await this.provider.estimateResources(transaction);
let gasLimit: BigNumber;
let storageLimit: BigNumber;
let totalLimit = await transaction.gasLimit;
if (totalLimit === null || totalLimit === undefined) {
gasLimit = resources.gas;
storageLimit = resources.storage;
totalLimit = resources.gas.add(resources.storage);
} else {
const estimateTotalLimit = resources.gas.add(resources.storage);
gasLimit = BigNumber.from(totalLimit).mul(resources.gas).div(estimateTotalLimit).add(1);
storageLimit = BigNumber.from(totalLimit).mul(resources.storage).div(estimateTotalLimit).add(1);
}
transaction.gasLimit = totalLimit;
const tx = await this.populateTransaction(transaction);
const data = tx.data;
const from = tx.from;
if (!data) {
return logger.throwError('Request data not found');
}
if (!from) {
return logger.throwError('Request from not found');
}
let extrinsic: SubmittableExtrinsic<'promise'>;
// @TODO create contract
if (!tx.to) {
extrinsic = this.provider.api.tx.evm.create(
tx.data,
toBN(tx.value),
toBN(gasLimit),
toBN(storageLimit.isNegative() ? 0 : storageLimit),
tx.accessList || []
);
} else {
extrinsic = this.provider.api.tx.evm.call(
tx.to,
tx.data,
toBN(tx.value),
toBN(gasLimit),
toBN(storageLimit.isNegative() ? 0 : storageLimit),
tx.accessList || []
);
}
await extrinsic.signAsync(signerAddress);
return new Promise((resolve, reject) => {
extrinsic
.send((result: SubmittableResult) => {
handleTxResponse(result, this.provider.api)
.then(() => {
resolve({
hash: extrinsic.hash.toHex(),
from: from || '',
confirmations: 0,
nonce: toBN(tx.nonce).toNumber(),
gasLimit: BigNumber.from(tx.gasLimit || '0'),
gasPrice: BigNumber.from(1),
data: dataToString(data),
value: BigNumber.from(tx.value || '0'),
chainId: +this.provider.api.consts.evmAccounts.chainId.toString(),
wait: (confirmations?: number): Promise<TransactionReceipt> => {
const hex = result.status.isInBlock
? result.status.asInBlock.toHex()
: result.status.asFinalized.toHex();
return this.provider.getTransactionReceiptAtBlock(extrinsic.hash.toHex(), hex);
}
});
})
.catch(reject);
})
.catch(reject);
});
}