Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace create collection with raw API call to faciliate api key #41

Merged
merged 5 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ REGISTRATION_ADDRESS=0xDbA6129C02E69405622fAdc3d5A7f8d23eac3b97
GAS_LIMIT=7000000
GAS_PRICE=40000000000

# Your environment's api key generated from https://hub.immutable.com/
API_KEY=
#=============================================
# Bulk Minting
#=============================================
Expand Down
84 changes: 79 additions & 5 deletions src/libs/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {
AlchemyProvider,
JsonRpcProvider,
Networkish,
} from '@ethersproject/providers';
import { Signer } from '@ethersproject/abstract-signer';
import { AlchemyProvider, JsonRpcProvider } from '@ethersproject/providers';
import BN from 'bn.js';
import * as encUtils from 'enc-utils';

export function wait(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
Expand Down Expand Up @@ -58,3 +57,78 @@
},
);
}

type SignatureOptions = {
r: BN;
s: BN;
recoveryParam: number | null | undefined;
};

// used to sign message with L1 keys. Used for registration
function serializeEthSignature(sig: SignatureOptions): string {
cynsupercat marked this conversation as resolved.
Show resolved Hide resolved
// This is because golang appends a recovery param
// https://github.com/ethers-io/ethers.js/issues/823
return encUtils.addHexPrefix(
encUtils.padLeft(sig.r.toString(16), 64) +
encUtils.padLeft(sig.s.toString(16), 64) +

Check failure on line 73 in src/libs/utils.ts

View workflow job for this annotation

GitHub Actions / Lint

Insert `··`
encUtils.padLeft(sig.recoveryParam?.toString(16) || '', 2),

Check failure on line 74 in src/libs/utils.ts

View workflow job for this annotation

GitHub Actions / Lint

Insert `··`
);
}

function importRecoveryParam(v: string): number | undefined {
const isValidBigNumber =
new BN(v, 16).cmp(new BN(27)) !== -1
? new BN(v, 16).sub(new BN(27)).toNumber()
: new BN(v, 16).toNumber();
return v.trim() ? isValidBigNumber : undefined;
}

// used chained with serializeEthSignature. serializeEthSignature(deserializeSignature(...))
function deserializeSignature(sig: string, size = 64): SignatureOptions {
const removedHexPrefixSig = encUtils.removeHexPrefix(sig);
return {
r: new BN(removedHexPrefixSig.substring(0, size), 'hex'),
s: new BN(removedHexPrefixSig.substring(size, size * 2), 'hex'),
recoveryParam: importRecoveryParam(
removedHexPrefixSig.substring(size * 2, size * 2 + 2),
),
};
}

export async function signRaw(
payload: string,
signer: Signer,
): Promise<string> {
const signature = deserializeSignature(await signer.signMessage(payload));
return serializeEthSignature(signature);
}

type IMXAuthorisationHeaders = {
timestamp: string;
signature: string;
};

export async function generateIMXAuthorisationHeaders(
ethSigner: Signer,
): Promise<IMXAuthorisationHeaders> {
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = await signRaw(timestamp, ethSigner);

return {
timestamp,
signature,
};
}

export async function signMessage(
message: string,
signer: Signer,
): Promise<{ message: string; ethAddress: string; ethSignature: string }> {
const ethAddress = await signer.getAddress();
const ethSignature = await signRaw(message, signer);
return {
message,
ethAddress,
ethSignature,
};
}
50 changes: 32 additions & 18 deletions src/onboarding/3-create-collection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Wallet } from '@ethersproject/wallet';
import { ImLogger, WinstonLogger } from '@imtbl/imlogging';
import { CreateCollectionParams, ImmutableXClient } from '@imtbl/imx-sdk';
import { getProvider, requireEnvironmentVariable } from 'libs/utils';
import axios from 'axios';
import {
generateIMXAuthorisationHeaders,
getEnv,
getProvider,
requireEnvironmentVariable,
} from 'libs/utils';

import env from '../config/client';
import { loggerConfig } from '../config/logging';
Expand All @@ -22,18 +27,15 @@ const component = '[IMX-CREATE-COLLECTION]';
const signer = wallet.connect(provider);
const ownerPublicKey = wallet.publicKey;

const user = await ImmutableXClient.build({
...env.client,
signer,
enableDebug: true,
});

log.info(component, 'Creating collection...', collectionContractAddress);

/**
* Edit your values here
*/
const params: CreateCollectionParams = {
const { timestamp, signature } = await generateIMXAuthorisationHeaders(
signer,
);
const createCollectionRequest = {
/**
* Edit your values here
*/
name: 'ENTER_COLLECTION_NAME',
// description: 'ENTER_COLLECTION_DESCRIPTION (OPTIONAL)',
contract_address: collectionContractAddress,
Expand All @@ -44,15 +46,27 @@ const component = '[IMX-CREATE-COLLECTION]';
project_id: parseInt(projectId, 10),
};

let collection;
try {
collection = await user.createCollection(params);
} catch (error) {
throw new Error(JSON.stringify(error, null, 2));
const headers: Record<string, string> = {
'Content-type': 'application/json',
'IMX-Signature': signature,
'IMX-Timestamp': timestamp,
};

const apiKey = getEnv('API_KEY');
if (apiKey) {
headers['x-immutable-api-key'] = apiKey;
}

const resp = await axios.post(
`${getEnv('PUBLIC_API_URL')}/collections`,
createCollectionRequest,
{
headers,
},
);

log.info(component, 'Created collection');
console.log(JSON.stringify(collection, null, 2));
console.log(JSON.stringify(resp.data, null, 2));
})().catch(e => {
log.error(component, e);
process.exit(1);
Expand Down
Loading