본문으로 건너뛰기

Aptos

November 11, 2022
Nahee Park
Software Engineer, DSRV

Prerequisites

Aptos 파트에서는 aptos를 추가로 이용하여 실습을 진행합니다. 해당 패키지를 미리 설치해주세요.

Signed transaction 생성하기​

signed transaction을 만들기 위해선 크게 세 가지 절차를 거쳐야 합니다.

  1. raw transaction 생성
  2. raw transaction에 대한 signature 생성
  3. raw transaction과 signature을 합쳐 signed transaction 생성

1. getAptosTx​

signed transaction을 생성하기 위해서는 raw transaction과 signature가 필요합니다. kms를 통해 signature를 얻기 위해선 raw transaction이 필요하기 때문에 먼저 raw transaction을 생성하기 위한 getAptosTx 함수를 만듭니다.

getAptosTx.ts
import { AptosClient, TxnBuilderTypes, BCS } from 'aptos';
import { Account, CHAIN } from '@dsrv/kms/lib/types';
import { Aptos } from '@dsrv/kms/lib/blockchains/aptos';
import { sha3_256 } from 'js-sha3';

interface RawTransaction {
unSignedTx: TxnBuilderTypes.RawTransaction;
serializedTx: string;
}
const aptos = new AptosClient('https://fullnode.devnet.aptoslabs.com/v1');

const getAccountExists = async (address: string) => {
try {
await aptos.getAccountResources(address);
} catch (error) {
const chainId = await aptos.getChainId();
const url = `https://faucet.${
chainId === 2 ? 'testnet' : 'devnet'
}.aptoslabs.com/mint?amount=0&address=${address.replace('0x', '')}`;
// eslint-disable-next-line no-undef
await fetch(url, { method: 'POST' });
await aptos.getAccountResources(address);
}
};

export const getAptosTx = async (mnemonic: string) => {
const MAX_GAS_AMOUNT = 150;
const GAS_UNIT_PRICE = 100;
const amount = 100;

/* 1. get Account */
const account = Aptos.getAccount({
mnemonic,
path: { type: CHAIN.APTOS, account: 0, index: 0 },
});

await getAccountExists(account.address);
const { sequence_number: sequenceNumber } = await aptos.getAccount(account.address);

/* 2. make raw transaction */
const chainId = await aptos.getChainId();
const expirationTimestampSecs = Math.floor(Date.now() / 1000) + 300;

const token = new TxnBuilderTypes.TypeTagStruct(
TxnBuilderTypes.StructTag.fromString('0x1::aptos_coin::AptosCoin'),
);

const entryFunctionPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural(
'0x1::coin',
'transfer',
[token],
[
BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(account.address)),
BCS.bcsSerializeUint64(amount),
],
),
);

const rawTxn = new TxnBuilderTypes.RawTransaction(
TxnBuilderTypes.AccountAddress.fromHex(account.address),
BigInt(sequenceNumber),
entryFunctionPayload,
BigInt(MAX_GAS_AMOUNT),
BigInt(GAS_UNIT_PRICE),
BigInt(expirationTimestampSecs),
new TxnBuilderTypes.ChainId(chainId),
);

const rawTxnWithSalt = `0x${Buffer.concat([
Buffer.from(sha3_256(Buffer.from('APTOS::RawTransaction', 'ascii')), 'hex'),
Buffer.from(BCS.bcsToBytes(rawTxn)),
]).toString('hex')}`;

return {
serializedTx: rawTxnWithSalt,
unSignedTx: rawTxn,
};
};

2. getAptosSignature​

다음으로 transaction에 대한 signature를 얻기 위해, serializedTx를 인자로 받아서 signature를 생성하는 getAptosSignature 함수를 만듭니다.

getAptosSignature.ts
import { CHAIN } from '@dsrv/kms/lib/types';
import { Aptos } from '@dsrv/kms/lib/blockchains/aptos';

export const getAptosSignature = (serializedTx: string): string => {
const { signature } = Aptos.signTx(
{
mnemonic,
path: { type: CHAIN.APTOS, account: 0, index: 0 },
},
serializedTx,
);

return signature;
};

3. createAptosSignedTx​

마지막으로 위에서 생성한 serializedTx와 signature, 그리고 계정 생성을 위한 mnemonic 통해 signed transaction을 리턴해주는 createAptosSignedTx 함수를 만듭니다.

createAptosSignedTx.ts
import { TxnBuilderTypes, BCS } from 'aptos';
import { Aptos } from '@dsrv/kms/lib/blockchains/aptos';
import { CHAIN } from '@dsrv/kms/lib/types';

interface createAptosSignedTxProps {
serializedTx: string;
signature: string;
mnemonic: string;
}

/* create singedTx by combining tx and signature */
export const createAptosSignedTx = ({
serializedTx,
signature,
mnemonic,
}: createAptosSignedTxProps): string => {
const account = Aptos.getAccount({
mnemonic,
path: { type: CHAIN.APTOS, account: 0, index: 0 },
});
const rawTxn = TxnBuilderTypes.RawTransaction.deserialize(
new BCS.Deserializer(Buffer.from(serializedTx.replace('0x', '').slice(64), 'hex')),
);

const signed = new TxnBuilderTypes.SignedTransaction(
rawTxn,
new TxnBuilderTypes.TransactionAuthenticatorEd25519(
new TxnBuilderTypes.Ed25519PublicKey(Buffer.from(account.publicKey.replace('0x', ''), 'hex')),
new TxnBuilderTypes.Ed25519Signature(Buffer.from(signature.replace('0x', ''), 'hex')),
),
);

return signed;
};

최종적으로 위에서 만든 함수, getAptosTx, getAptosSignature, createAptosSignedTx 를 이용해 signed transaction을 리턴하는 getAptosSignedTx를 만들 수 있습니다.

getAptosSignedTx.ts
export const getAptosSignedTx = async (mnemonic: string) => {
/* 1. get rawTransaction */
const { serializedTx, unSignedTx } = await getAptosTx(mnemonic);
/* 2. get signature */
const aptosSignature = getAptosSignature(serializedTx);
/* 3. create singedTx by combining rawTransaction and signature */
const aptosSignedTx = createAptosSignedTx({
unSignedTx,
signature: aptosSignature,
mnemonic,
});

return aptosSignedTx;
};

Signed transaction 전송하기​

Signed transaction을 생성했다면, 그것을 이용하여 트랜잭션을 전송할 수 있습니다.

sendAptosTransaction.ts
import { TxnBuilderTypes, BCS } from 'aptos';

export const sendAptosTransaction = async (signedTx: TxnBuilderTypes.SignedTransaction) => {
const rpcUrl = 'https://fullnode.devnet.aptoslabs.com/v1';

const resp = await fetch(`${rpcUrl}/transactions`, {
method: 'POST',
headers: {
// https://github.com/aptos-labs/aptos-core/blob/e7d5f952afe3afcf5d1415b67e167df6d49019bf/ecosystem/typescript/sdk/src/aptos_client.ts#L327
'Content-Type': 'application/x.aptos.signed_transaction+bcs',
},
body: BCS.bcsToBytes(signedTx),
});

const result = await resp.json();

return result;
};
main.ts
const mnemonic = 'sample mnemonic';
const main = async () => {
const aptosSignedTx = await getAptosSignedTx(mnemonic);
const aptosTxResult = await sendAptosTransaction(aptosSignedTx);
console.log('Aptos Tx Result : ', aptosTxResult);
};
main();

Examples​

아래의 예제를 통해 실제로 트랜잭션을 전송해 볼 수 있습니다. 트랜잭션을 보내기 위해선 테스트넷 토큰이 필요합니다. 지갑 내의 FAUCET 탭에서 faucet을 요청할 수 있습니다.

위험

니모닉이 유출될 경우, 암호화폐 자산을 모두 잃을 수 있습니다. 아래의 예제를 실행시킬 때에는 테스트용 혹은 개발용 니모닉을 사용해주세요.

라이브 에디터
결과
Loading...