Skip to content

Commit

Permalink
Merge pull request #473 from casper-ecosystem/add-transaction-builders
Browse files Browse the repository at this point in the history
Add transaction builders
  • Loading branch information
alexmyshchyshyn authored Dec 20, 2024
2 parents 17df26c + 098fcde commit b52d9af
Show file tree
Hide file tree
Showing 8 changed files with 928 additions and 90 deletions.
83 changes: 21 additions & 62 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,81 +114,40 @@ Example of how to construct a transaction and push it to the network:

```ts
import {
Args,
CLValue,
CLValueOption,
CLValueUInt64,
CLValueUInt512,
Duration,
HttpHandler,
InitiatorAddr,
KeyAlgorithm,
PricingMode,
PrivateKey,
PublicKey,
RpcClient,
Timestamp,
TransactionEntryPoint,
TransactionScheduling,
TransactionTarget,
TransactionV1,
TransactionV1Payload,
TransactionEntryPointEnum,
PaymentLimitedMode
NativeTransferBuilder,
PrivateKey,
KeyAlgorithm,
PublicKey
} from 'casper-js-sdk';

const rpcHandler = new HttpHandler('http://<Node Address>:7777/rpc');
const rpcClient = new RpcClient(rpcHandler);

const privateKey = await PrivateKey.generate(KeyAlgorithm.ED25519);
const timestamp = new Timestamp(new Date());
const paymentAmount = '20000000000000';

const pricingMode = new PricingMode();
const paymentLimitedMode = new PaymentLimitedMode();
paymentLimitedMode.gasPriceTolerance = 1;
paymentLimitedMode.paymentAmount = paymentAmount;
paymentLimitedMode.standardPayment = true;
pricingMode.paymentLimited = paymentLimitedMode;

const args = Args.fromMap({
target: CLValue.newCLPublicKey(

const transaction = new NativeTransferBuilder()
.from(privateKey.publicKey)
.target(
PublicKey.fromHex(
'0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64'
)
),
amount: CLValueUInt512.newCLUInt512(paymentAmount),
id: CLValueOption.newCLOption(CLValueUInt64.newCLUint64(3)) // memo ( optional )
});
)
.amount('25000000000') // Amount in motes
.id(Date.now())
.chainName('casper-net-1')
.payment(100_000_000)
.build();

const transactionTarget = new TransactionTarget({}); // Native target;
const entryPoint = new TransactionEntryPoint(
TransactionEntryPointEnum.Transfer
);
const scheduling = new TransactionScheduling({}); // Standard;

const transactionPayload = TransactionV1Payload.build({
initiatorAddr: new InitiatorAddr(privateKey.publicKey),
ttl: new Duration(1800000),
args,
timestamp,
entryPoint,
scheduling,
transactionTarget,
chainName: 'casper-net-1',
pricingMode
});

const transactionV1 = TransactionV1.makeTransactionV1(
transactionPayload
);
await transactionV1.sign(privateKey);
await transaction.sign(privateKey);

const tx = Transaction.fromTransactionV1(transactionV1);

const result = await rpcClient.putTransaction(tx);

console.log(`Transaction Hash: ${result.transactionHash}`);
try {
const result = await rpcClient.putTransaction(transaction);
console.log(`Transaction Hash: ${result.transactionHash}`);
} catch (e) {
console.error(e);
}
```

### Creating a legacy deploy
Expand Down
93 changes: 69 additions & 24 deletions migration-guide-v2-v5.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,14 @@ const nativeTarget = new TransactionTarget({});
const stored = new StoredTarget();
const invocationTarget = new TransactionInvocationTarget();
invocationTarget.byHash = new Hash(Uint8Array.from([])); // an example of hash
storedTarget.runtime = 'VmCasperV1'; // an example of runtime
storedTarget.runtime = TransactionRuntime.vmCasperV1(); // an example of runtime
storedTarget.id = invocationTarget;

const storedTransactionTarget = new TransactionTarget(
undefined,
stored
);

// OR
const storedTransactionTarget1 = new TransactionTarget();
storedTransactionTarget1.stored = stored;
Expand All @@ -117,16 +118,15 @@ storedTransactionTarget1.stored = stored;
```typescript
const sessionTarget = new SessionTarget();
sessionTarget.moduleBytes = Uint8Array.from([]); // an example of module bytes
sessionTarget.runtime = 'VmCasperV1'; // an example of runtime
sessionTarget.transferredValue = 1000; // an example of transferredValue
sessionTarget.runtime = TransactionRuntime.vmCasperV1(); // an example of runtime
sessionTarget.isInstallUpgrade = true; // an example of isInstallUpgrade
sessionTarget.seed = new Hash(Uint8Array.from([])); // an example of seed

const sessionTransactionTarget = new TransactionTarget(
undefined,
undefined,
sessionTarget
);

// OR
const sessionTransactionTarget1 = new TransactionTarget();
sessionTransactionTarget1.session = sessionTarget;
Expand Down Expand Up @@ -158,19 +158,20 @@ Specifies how transaction fees are calculated. Supports three modes:

#### Examples:

- **FixedMode**:
- **PaymentLimitedMode**:

```typescript
const fixedMode = new FixedMode();
fixedMode.gasPriceTolerance = 2;
fixedMode.additionalComputationFactor = 1;
const paymentLimited = new PaymentLimitedMode();
paymentLimited.standardPayment = true;
paymentLimited.paymentAmount = '250000000';
paymentLimited.gasPriceTolerance = 1;
```

- **Assign Pricing Mode**:

```typescript
const pricingMode = new PricingMode();
pricingMode.fixed = fixedMode;
pricingMode.paymentLimited = paymentLimited;
```

---
Expand All @@ -197,10 +198,10 @@ const publicKey = privateKey.publicKey;

```typescript
const args = Args.fromMap({
target: CLValue.newCLPublicKey(
PublicKey.fromHex('<Recipient Public Key>')
),
amount: CLValueUInt512.newCLUInt512('2000000000') // 2 CSPR
target: CLValue.newCLPublicKey(
PublicKey.fromHex('<Recipient Public Key>')
),
amount: CLValueUInt512.newCLUInt512('2000000000') // 2 CSPR
});
```

Expand All @@ -216,7 +217,7 @@ const transactionTarget = new TransactionTarget({}); // Native target;

```typescript
const entryPoint = new TransactionEntryPoint(
TransactionEntryPointEnum.Transfer
TransactionEntryPointEnum.Transfer
);
```

Expand All @@ -242,19 +243,19 @@ pricingMode.paymentLimited = paymentLimitedMode;

```typescript
const transactionPayload = TransactionV1Payload.build({
initiatorAddr: new InitiatorAddr(publicKey),
ttl: new Duration(1800000),
args,
timestamp: new Timestamp(new Date()),
entryPoint,
scheduling,
transactionTarget,
chainName: 'casper-net-1',
pricingMode
initiatorAddr: new InitiatorAddr(publicKey),
ttl: new Duration(1800000),
args,
timestamp: new Timestamp(new Date()),
entryPoint,
scheduling,
transactionTarget,
chainName: 'casper-net-1',
pricingMode
});

const transaction = TransactionV1.makeTransactionV1(
transactionPayload
transactionPayload
);
await transaction.sign(privateKey);
```
Expand All @@ -265,3 +266,47 @@ await transaction.sign(privateKey);
const result = await rpcClient.putTransactionV1(transaction);
console.log(`Transaction Hash: ${result.transactionHash}`);
```

## Using [TransactionBuilder](./src/types/TransactionBuilder.md)

The `TransactionBuilder` is a base class used to create various types of transactions. By extending this class, you can build custom transaction types with specific methods and properties.

#### Example of how to construct a transaction with TransactionBuilder and push it to the network:

```ts
import {
HttpHandler,
RpcClient,
NativeTransferBuilder,
PrivateKey,
KeyAlgorithm,
PublicKey
} from 'casper-js-sdk';

const rpcHandler = new HttpHandler('http://<Node Address>:7777/rpc');
const rpcClient = new RpcClient(rpcHandler);

const privateKey = await PrivateKey.generate(KeyAlgorithm.ED25519);

const transaction = new NativeTransferBuilder()
.from(sender.publicKey)
.target(
PublicKey.fromHex(
'0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64'
)
)
.amount('25000000000') // Amount in motes
.id(Date.now())
.chainName('casper-net-1')
.payment(100_000_000)
.build();

await transaction.sign(privateKey);

try {
const result = await rpcClient.putTransaction(transaction);
console.log(`Transaction Hash: ${result.transactionHash}`);
} catch (e) {
console.error(e);
}
```
4 changes: 3 additions & 1 deletion src/types/Deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,9 @@ export class Deploy {
* @param keys The private key used to sign the deploy.
*/
public async sign(keys: PrivateKey): Promise<void> {
const signatureBytes = await keys.sign(this.hash.toBytes());
const signatureBytes = await keys.signAndAddAlgorithmBytes(
this.hash.toBytes()
);
const signature = new HexBytes(signatureBytes);
this.approvals.push(new Approval(keys.publicKey, signature));
}
Expand Down
29 changes: 29 additions & 0 deletions src/types/Transaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
CLValueUInt64
} from './clvalue';
import { TransactionV1Payload } from './TransactionV1Payload';
import { NativeTransferBuilder } from './TransactionBuilder';

describe('Test Transaction', () => {
it('should create a TransactionV1 with correct payload instance', async () => {
Expand Down Expand Up @@ -90,4 +91,32 @@ describe('Test Transaction', () => {
expect(transaction.payload.fields.scheduling).to.deep.equal(scheduling);
expect(transaction.payload.fields.entryPoint).to.deep.equal(entryPoint);
});

it('should create native transfer TransactionV1 with builder', async () => {
const sender = await PrivateKey.generate(KeyAlgorithm.ED25519);

const transaction = new NativeTransferBuilder()
.from(sender.publicKey)
.target(
PublicKey.fromHex(
'0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64'
)
)
.amount('25000000000')
.id(Date.now())
.chainName('casper-net-1')
.payment(100_000_000)
.build();

await transaction.sign(sender);

const transactionV1 = transaction.getTransactionV1()!;
const transactionPaymentAmount = transactionV1.payload.fields.args.args
.get('amount')!
.toString();

assert.deepEqual(transaction.approvals[0].signer, sender.publicKey);
assert.deepEqual(parseInt(transactionPaymentAmount, 10), 25000000000);
expect(transactionV1.payload.chainName).to.deep.equal('casper-net-1');
});
});
13 changes: 10 additions & 3 deletions src/types/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ export class TransactionV1 {
* @param keys The private key to sign the transaction.
*/
async sign(keys: PrivateKey): Promise<void> {
const signatureBytes = await keys.sign(this.hash.toBytes());
const signatureBytes = await keys.signAndAddAlgorithmBytes(
this.hash.toBytes()
);
const signature = new HexBytes(signatureBytes);

if (!this.approvals) {
Expand Down Expand Up @@ -467,7 +469,10 @@ export class Transaction {
}

public getTransactionWrapper(): TransactionWrapper {
return new TransactionWrapper(this.originDeployV1, this.originTransactionV1);
return new TransactionWrapper(
this.originDeployV1,
this.originTransactionV1
);
}

/**
Expand All @@ -489,7 +494,9 @@ export class Transaction {
* @param key The private key to sign the transaction.
*/
async sign(key: PrivateKey): Promise<void> {
const signatureBytes = await key.sign(this.hash.toBytes());
const signatureBytes = await key.signAndAddAlgorithmBytes(
this.hash.toBytes()
);
this.setSignature(signatureBytes, key.publicKey);
}

Expand Down
Loading

0 comments on commit b52d9af

Please sign in to comment.