Skip to content

Instantly share code, notes, and snippets.

@abrkn
Created July 4, 2020 11:24
Show Gist options
  • Save abrkn/2e74afd5b82b37661cb03691ebb4e433 to your computer and use it in GitHub Desktop.
Save abrkn/2e74afd5b82b37661cb03691ebb4e433 to your computer and use it in GitHub Desktop.
Move the remaining balance of a Tezos account to the specified destination
import * as Conseil from 'conseiljs';
import { SoftSigner } from 'conseiljs-softsigner';
import { ns } from '@sideshift/shared';
import {
initializeConseil,
TezosRpcClient,
getTezosKeyStoreFromSecret,
createLogger,
} from '@sideshift/shared-node';
// TODO: Remove magic constants
const DRAIN_COST = 50;
const CHAIN_ID = 'NetXdQprcVkpaWU';
export type DrainTezosAccountOptions = {
/**
* The secret key of the source account.
*/
secret: string;
destination: string;
rpc: TezosRpcClient;
/**
* The account used to estimate the fee. This can be any account that has
* a non-zero balance and is not a smart contract. The account will not
* be used in the actual operations submitted to the Tezos blockchain.
*/
estimateAccount: string;
};
/**
* Move the remaining balance of a Tezos account to the specified destination.
* @returns The operation group id when the source account has a non-zero balance else undefined
*/
export const drainTezosAccount = async ({
secret,
destination,
rpc,
estimateAccount,
}: DrainTezosAccountOptions): Promise<string | undefined> => {
const logger = createLogger({ name: 'tezos' });
initializeConseil();
const keyStore = await getTezosKeyStoreFromSecret(secret);
const signer = new SoftSigner(keyStore.secretKeyRaw);
const balanceUnits = await rpc.getBalance(keyStore.publicKeyHash);
if (ns.eq(balanceUnits, 0)) {
return undefined;
}
logger.debug(`Balance of ${keyStore.publicKeyHash}: ${balanceUnits} mutez`);
const isSourceRevealed = await Conseil.TezosNodeReader.isManagerKeyRevealedForAccount(
rpc.url,
keyStore.publicKeyHash
);
const prevCounterDestination = await Conseil.TezosNodeReader.getCounterForAccount(
rpc.url,
keyStore.publicKeyHash
);
const prevCounterEstimate = await Conseil.TezosNodeReader.getCounterForAccount(
rpc.url,
estimateAccount
);
const reveralOperation: Conseil.Reveal | undefined = isSourceRevealed
? undefined
: {
kind: 'reveal',
source: keyStore.publicKeyHash,
fee: '0', // Reveal Fee will be covered by the appended operation
counter: (prevCounterDestination + 1).toString(),
gas_limit: '10000',
storage_limit: '0',
public_key: keyStore.publicKey,
};
const transactionOperationBase = {
destination,
storage_limit: '0',
kind: 'transaction',
};
const transactionEstimateOperation: Conseil.Transaction = {
...transactionOperationBase,
amount: balanceUnits.toString(),
source: estimateAccount,
gas_limit: '800000',
fee: '5000',
counter: (prevCounterEstimate + 1).toString(),
};
logger.debug(`Estimating transaction cost...`);
const transactionEstimate = await Conseil.TezosNodeWriter.estimateOperation(
rpc.url,
CHAIN_ID,
transactionEstimateOperation
);
const cost = transactionEstimate.gas + transactionEstimate.storageCost + DRAIN_COST;
logger.debug(`Estimated transaction cost: ${cost} mutez`);
const transactionCounter = (isSourceRevealed
? prevCounterDestination + 1
: prevCounterDestination + 2
).toString();
const transactionOperation: Conseil.Transaction = {
...transactionOperationBase,
amount: ns.minus(balanceUnits, cost).toString(),
source: keyStore.publicKeyHash,
gas_limit: cost.toString(),
fee: cost.toString(),
counter: transactionCounter,
};
const operations: Conseil.Operation[] = [transactionOperation];
if (reveralOperation) {
operations.unshift(reveralOperation);
}
logger.debug(`Submitting transaction...`);
const response = await Conseil.TezosNodeWriter.sendOperation(
rpc.url,
operations,
(signer as unknown) as Conseil.Signer
);
const operationGroupId = JSON.parse(response.operationGroupID);
logger.debug(`Drained ${keyStore.publicKeyHash} in ${operationGroupId}`);
return operationGroupId;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment