Create #OneTap links to pay/buy/mint on-chain in 5min!
1️⃣ Mint Your Free Developer Card
- Start your journey Minting an Acme Developer Card:
- Log in with your developer email upon clicking the above link.
2️⃣ Update Your Profile
Configure your identity to help users recognize you when you share your links with them:
- Go to your Acme Vault:
- Mainnet: https://app.acme.am
- Testnet: https://staging.app.acme.am
- Navigate to Settings -> Edit Profile
- Update your Username and Profile Picture to reflect your identity.
3️⃣ Store API Keys Safely
Ensure your API keys are securely stored for easy access:
- Go to your Acme Vault:
- Mainnet: https://app.acme.am
- Testnet: https://staging.app.acme.am
- Go to Settings -> API Keys
- Copy your API keys under "Production" and paste them in Acme's README platform or directly in your IDE.
- Use the following domain to call Acme APIs:
4️⃣ Create Intent Links
Create links to help your users directly transact on-chain in #OneTap:
🪙 Minting
Deploy & distribute tokens to users:
- Deploy Contract:
- Use Deploy NFT or Deploy ERC-20 API.
- Copy the
orderId
from the response. - Query the
orderStatus
using the Get Order endpoint. - Extract the
address
field from thedeployedSmartContract
section of the status response.
- Create Minting Link:
- Generate minting links using the retrieved
address
: Create Minting Link for NFT or Create Minting Link for ERC-20 Tokens. - Copy the link from the response and share it with your users.
- Generate minting links using the retrieved
💰 Payments
Request on-chain payments from user wallets to your own:
- Create a payment link using the Create Payment Link API.
- Copy the link from the response and share it with your users.
📈 Trades
Help users buy tokens on-chain and earn affiliate fees:
- Create Buy Link: Generate a token referral link for user-specified purchase amounts.
- Create Buy Link with Amount: Create a link for buying a pre-fixed amount of tokens in USDC.
- Create Sell Link with Amount: Create a link for selling a pre-fixed amount of tokens for USDC.
5️⃣ Register Your Webhook
Stay updated with real-time notifications:
- Register your webhook using Set WebHook to receive server updates whenever users fulfill intents.
- Here's the structure of a webhook response:
{
"Order": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"description": "Unique identifier for the order.",
"example": "ca7fc2a9-4e7f-4580-9caf-7026d4c0ca3e"
},
"status": {
"type": "string",
"description": "Current status of the order.",
"enum": [
"Initial",
"ProcessingFiatProviderOrder",
"PaymentReceivedOnFiatProvider",
"BlockchainTransactionSubmission",
"Completed",
"Failed"
],
"example": "Completed"
},
"createdAt": {
"type": "string",
"format": "date-time",
"description": "Timestamp when the order was created.",
"example": "2024-08-05T15:01:19.447Z"
},
"blockchainTransactionHash": {
"type": "string",
"description": "Hash of the blockchain transaction associated with the order.",
"example": "0x11e60b014f3d9544b4023e013c0556d9bec2ec4edcd3891c7bcef1539a7e3c7f"
},
"executionMessage": {
"type": ["string", "null"],
"description": "Message related to the execution of the order, if any.",
"example": null
},
"intentId": {
"type": "string",
"format": "uuid",
"description": "Unique identifier for the intent associated with the order.",
"example": "bdecde47-85c1-405d-b2fb-1c971254e0d4"
},
"intentMemo": {
"type": ["string", "null"],
"description": "Additional memo from the dev associated with the intent.",
"example": null
},
"userId": {
"type": "string",
"format": "uuid",
"description": "Unique identifier for the user who created the order.",
"example": "41a12c53-aef6-463c-b707-a0c4e79711c9"
}
}
}
}
- Use the following script to validate Acme signatures included in the headers of each webhook notification:
import crypto, { BinaryToTextEncoding } from "node:crypto";
const fs = require("fs");
const stagePublicKey =
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyocie+Q4+AGH7u4ZtpI0\nt+CSsfQiNHvvcWm4ztROV/08Tf3IP2vWFqktZYMPBwQ6z+lYBpOW6NkjWm/eOAkH\nIfWScW59mK2XzeMkBZc03voH4TBZGg5uU/AszuROcDBU344UX4tmbbAVnJP9y8aQ\nIMvVEYa4BOqAEMwRWEAej30slG3tBvilvJTdUMFkN405fnUIVMEjW9shH8mtSSX3\nkw/TW2Y1PB72j64Mw0aBw93vtrKU2UzON9KsOB7Sih56EYnAsPsN1ix42tP0ozyw\nYo02zf6sy59OroeYP0NGNjebYbvQQvSc7/7o4R3/TxKMPAsSsb0EpQjDpodV8jwf\nJQIDAQAB\n-----END PUBLIC KEY-----";
const prodPublicKey =
"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxn/AHYRfPB5lA2gpV4Hn\nBsvHwY2iRQaEIvX3mOfXqFhzOvGA2aM/klDAhZkZgtkS8AtOIgP0cZ4LsbkYQBIV\nY8YWNy88NNte4rJzO/vtxLHdvgfMIohuduVNhCZAjNPC23nc4/tYsaaW/g0cJaPQ\nfQzM3jit6EQ8Oo0FNu5EZYmPnpusbB0XyTnS6iwF/+YIbkRhwivzJVNS7mO8b8vT\nr5y7eV/KD+CiMC2+/1bTyUTeFI4gtrGoODcNfSCbA7ShtJJoS6AJGx9VOTuML5dD\nzpU75WlG0+mQFqIzO1RtGQWKELxppc5Ws+ZN/0dM5KTaKVdL4TJe+h/K4WZrmHDT\nXv/rBc8nRfPJ6sH+44wNqUJrML6iNFkIh0h1f7Z8tU9inmedg+8bxSCb2xp8XCyV\n2k9+i/j6CQBkC0YzEBJBWl5DK9N5Dq8phrhX8KPuP4uz9O/s3jlBQ478Uqx4csGr\n5ipqijCnzzwbZLRUdf7cJf/tku5Dhnya/n9ENybf/P1N/VN5bjPFfGvkWJxOwjK1\nPeebWudSdyChrSSPhT4WRdssOCucx0bHRA+xgKQX5bAL7mktQLZIbKMjKt3Bdj2e\nFvO4H1eVNziOHVhuCBJxRfju+JPg4JNjGzX3caAd427Gzir8EPoYRpotcd0VEVUV\nwWaV1p6MVdCEC+3z8V6pAD8CAwEAAQ==\n-----END PUBLIC KEY-----";
const publicKey =
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArojGIZ5IPLPDSx/AfKW6\nRMxBVhZp7Ze7EnqWGVBe+RxPNW2+vKG+cZ7GdgA/SemkZc6uh0IQSwJoXoZbwr8I\nyDzPSQT9wdO1vltBSE158lb08Pfg7LWUUsXF/H45YCHikyyCvsrbjQEoEoUh25k4\n3pjVa8431xAe4aukrWA+Fl0D4Izkzyzbhhzt4zcmcDzNJoe34hH1zn2fkH+bG/8s\ncuFmZxuAp286SVn0vfUIZj5y4La+FhiFlQCgmxw4TTIwuXZ2MZLYefO31uZ0qa0R\nzy/9S2WRrD4b7K1LKZ8ZSTBF4FfA0v5kBjrpLX4B3SVestMJo5IK1dDUfSKN9bRu\ntwIDAQAB\n-----END PUBLIC KEY-----".replace(
/\\n/g,
"\n"
);
export const DEFAULT_HASH_CONFIG = {
hashAlgorithm: "sha512",
signAlgorithm: "RSA-SHA512",
encodedSignatureAlgorithm: "base64" as BinaryToTextEncoding,
};
export const validateSignature = (params: {
publicKey: string;
message: string;
signature: string;
config?: {
hashAlgorithm: "sha512";
signAlgorithm: "RSA-SHA512";
encodedSignatureAlgorithm: BinaryToTextEncoding;
};
}): boolean => {
const config = params.config ?? DEFAULT_HASH_CONFIG;
const verifier = crypto.createVerify(config.signAlgorithm);
const hashedMessage = crypto
.createHash(config.hashAlgorithm)
.update(params.message)
.digest();
verifier.write(hashedMessage);
verifier.end();
const isVerified = verifier.verify(
params.publicKey,
params.signature,
config.encodedSignatureAlgorithm
);
return isVerified;
};
//Body taken from notification body
const _message = {
order: {
id: "32adfcea-ff26-48ce-a6dc-fbbcc8e68b7d",
status: "BlockchainTransactionSubmission",
createdAt: "2024-05-03T02:07:12.003Z",
blockchainTransactionHash: null,
executionMessage: null,
intentId: "8d6f6f80-11f6-48b2-a4e3-615a875928fd",
intentMemo: null,
userId: "4cf32778-07a5-4f33-8d58-eaaf41a9aeb4",
},
};
//Signature taken from notification header -> acme-signature
const _signature =
"OidfByNLdWowQYcx+w0KSE4utCjJmtZYwO+9fT6K2r6ahpKTgzm/iNwfhvLNxiWfD/1nGHzuOQdWrBqiXbkxZPBLiCpD8dJil2h23agJndd7aXtjqQ5qo64ZE0F+o8hkN/1695s6755G41MTzcucaH0gJo2mAeMG3gzCXQxa7NbmwqU5R8qrP6S8e+G5lcFSW3eT6pc8G+ylHsZusetHUKRMH6xo9sNcwYSCt5NE2yml1NtQYGKGe38L8ehnFbSuCbLRUY3bvBQRQFJhZXCfDOP+C40YdwEtepLrbCJ9fCMd8J/kc03HWICQ5ZjHsmnE/CnHVZ/UOcMIIxKz1AM0dA==";
console.log(
validateSignature({
publicKey: publicKey,
message: JSON.stringify(_message),
signature: _signature,
})
);