Skip to content
This repository has been archived by the owner on May 16, 2024. It is now read-only.

CORE-2034 add on-chain registration workflow #437

Merged
merged 7 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [4.0.0] - 2024-02-27
brayansdt marked this conversation as resolved.
Show resolved Hide resolved

### Added

- Added on chain registration workflow for StarkEx V4 contract.
- Update `StarkSigner` interface to add 2 methods - `signMessage` and `getYCoordinate`.

## [3.3.0] - 2024-02-27

### Added
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@imtbl/core-sdk",
"version": "3.3.0",
"version": "4.0.0",
brayansdt marked this conversation as resolved.
Show resolved Hide resolved
"description": "Immutable Core SDK",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
Expand Down
12 changes: 12 additions & 0 deletions src/ImmutableX.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,18 @@ export class ImmutableX {
});
}

/**
* Register a User to StarkEx contract if they are not already
kaihirota marked this conversation as resolved.
Show resolved Hide resolved
* @param walletConnection - the pair of L1/L2 signers
* @returns a promise that resolves with void if successful
* @throws {@link index.IMXError}
*/
public registerOnchain(walletConnection: WalletConnection) {
return this.workflows.registerOnchain(walletConnection).catch(err => {
throw formatError(err);
});
}

/**
* Checks if a User is registered on on-chain
* @param walletConnection - the pair of L1/L2 signers
Expand Down
15 changes: 15 additions & 0 deletions src/types/signers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Signer as EthSigner } from '@ethersproject/abstract-signer';
import { ec } from 'elliptic';

export { EthSigner };

Expand All @@ -13,11 +14,25 @@ export interface StarkSigner {
* @returns the signed prefixed-message
*/
signMessage(message: string): Promise<string>;

/**
* Get the Signer address
* @returns the Signer's checksum address
*/
getAddress(): string | Promise<string>;

/**
* Signs the prefixed-message
* @params message - this must be a UTF8-message
* @returns the signed prefixed-message
*/
sign(message: string): Promise<ec.Signature>;

/**
* Get the Y-coordinate of the public key
* @returns the Y-coordinate of the public key
*/
getYCoordinate(): string;
}

/**
Expand Down
26 changes: 26 additions & 0 deletions src/utils/stark/starkSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ export class StandardStarkSigner implements StarkSigner {
);
}

public async sign(msg: string): Promise<ec.Signature> {
return this.keyPair.sign(this.fixMsgHashLen(msg));
}

public getYCoordinate(): string {
return encUtils.sanitizeBytes(
this.keyPair.getPublic().getY().toString(16),
2,
);
}

/*
The function _truncateToN in lib/elliptic/ec/index.js does a shift-right of delta bits,
if delta is positive, where
Expand Down Expand Up @@ -60,3 +71,18 @@ export class StandardStarkSigner implements StarkSigner {
export function createStarkSigner(starkPrivateKey: string): StarkSigner {
return new StandardStarkSigner(starkPrivateKey);
}

export function serializePackedSignature(
sig: ec.Signature,
pubY: string,
): string {
return encUtils.sanitizeHex(
encUtils.padLeft(sig.r.toString(16), 64) +
encUtils.padLeft(sig.s.toString(16), 64, '0') +
encUtils.padLeft(
new BN(encUtils.removeHexPrefix(pubY), 'hex').toString(16),
64,
'0',
),
);
}
58 changes: 55 additions & 3 deletions src/workflows/registration.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { ImmutableXConfiguration } from '..';
import {
UsersApi,
GetSignableRegistrationResponse,
RegisterUserResponse,
} from '../api';
import { WalletConnection } from '../types';
import { signRaw } from '../utils';
import { Registration } from '../contracts';
import { StarkSigner, WalletConnection } from '../types';
import { serializePackedSignature, signRaw, starkEcOrder } from '../utils';
import { Registration, CoreV4__factory } from '../contracts';
import { solidityKeccak256 } from 'ethers/lib/utils';
import BN from 'bn.js';
import * as encUtils from 'enc-utils';
import { ec } from 'elliptic';
import { TransactionResponse } from '@ethersproject/providers';

type registerOffchainWorkflowParams = WalletConnection & {
usersApi: UsersApi;
Expand Down Expand Up @@ -81,3 +87,49 @@ export async function getSignableRegistrationOnchain(
verification_signature: response.data.verification_signature,
};
}

export async function signRegisterEthAddress(
starkSigner: StarkSigner,
ethAddress: string,
starkPublicKey: string,
): Promise<string> {
const hash: string = solidityKeccak256(
['string', 'address', 'uint256'],
['UserRegistration:', ethAddress, starkPublicKey],
);
const msgHash: BN = new BN(encUtils.removeHexPrefix(hash), 16);
const modMsgHash: BN = msgHash.mod(starkEcOrder);
const signature: ec.Signature = await starkSigner.sign(
modMsgHash.toString(16),
);
const pubY: string = encUtils.sanitizeHex(starkSigner.getYCoordinate());
return serializePackedSignature(signature, pubY);
}

export async function registerOnchainWorkflow(
walletConnection: WalletConnection,
config: ImmutableXConfiguration,
): Promise<TransactionResponse> {
const ethAddress = await walletConnection.ethSigner.getAddress();
const starkPublicKey = await walletConnection.starkSigner.getAddress();

const signature = await signRegisterEthAddress(
walletConnection.starkSigner,
ethAddress,
starkPublicKey,
);

const contract = CoreV4__factory.connect(
config.ethConfiguration.coreContractAddress,
walletConnection.ethSigner,
);

const populatedTransaction =
await contract.populateTransaction.registerEthAddress(
ethAddress,
starkPublicKey,
signature,
);

return walletConnection.ethSigner.sendTransaction(populatedTransaction);
}
7 changes: 7 additions & 0 deletions src/workflows/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { Registration__factory } from '../contracts';
import {
isRegisteredOnChainWorkflow,
registerOffchainWorkflow,
registerOnchainWorkflow,
} from './registration';
import { mintingWorkflow } from './minting';
import { transfersWorkflow, batchTransfersWorkflow } from './transfers';
Expand Down Expand Up @@ -126,6 +127,12 @@ export class Workflows {
);
}

public async registerOnchain(walletConnection: WalletConnection) {
await this.validateChain(walletConnection.ethSigner);

return registerOnchainWorkflow(walletConnection, this.config);
}

public async registerOffchain(walletConnection: WalletConnection) {
await this.validateChain(walletConnection.ethSigner);

Expand Down
Loading