@divvi/referral-sdk
Version:
SDK for managing referrals in the Divvi ecosystem
129 lines • 5.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.InvalidAddressError = void 0;
exports.getReferralTag = getReferralTag;
exports.submitReferral = submitReferral;
const constants_1 = require("./constants");
const types_1 = require("./types");
// Helper function to validate Ethereum addresses
function isValidAddress(address) {
return /^0x[a-fA-F0-9]{40}$/.test(address);
}
// Helper function to pad hex string to 32 bytes (64 characters)
function padHex(hex) {
return hex.padStart(64, '0');
}
// Helper function to encode a single address
function encodeAddress(address) {
return padHex(address.slice(2).toLowerCase());
}
// Helper function to encode an array of addresses
function encodeAddressArray(addresses) {
// Calculate the offset to where the array data starts
// This offset accounts for all static fields that come BEFORE this dynamic array:
// - encodedUser: 32 bytes
// - encodedConsumer: 32 bytes
// - providers array offset (this field): 32 bytes
// Total static data before array content: 3 × 32 = 96 bytes
//
// NOTE: When updating the ABI structure, update this calculation
const arrayDataOffsetHex = (96).toString(16).padStart(64, '0');
// Encode the length of the array (32 bytes)
const arrayLengthHex = addresses.length.toString(16).padStart(64, '0');
// Encode each address (32 bytes each)
const addressesHex = addresses.map((addr) => encodeAddress(addr)).join('');
return arrayDataOffsetHex + arrayLengthHex + addressesHex;
}
/**
* Generates the referral tag for the Divvi referral system.
*
* @param params - The parameters for generating the referral tag.
* @param params.user - The user address that consented to the transaction. This is cryptographically verified on the backend to ensure accurate referral attribution.
* @param params.consumer - The consumer address.
* @param params.providers - An array of provider addresses. Defaults to an empty array.
* @returns The referral tag as a hex string.
*/
function getReferralTag({ user, consumer, providers = [], }) {
// Validate addresses
if (!isValidAddress(user)) {
throw new types_1.InvalidAddressError({ address: user });
}
if (!isValidAddress(consumer)) {
throw new types_1.InvalidAddressError({ address: consumer });
}
for (const provider of providers) {
if (!isValidAddress(provider)) {
throw new types_1.InvalidAddressError({ address: provider });
}
}
// Encode the data according to ABI encoding rules
const encodedUser = encodeAddress(user);
const encodedConsumer = encodeAddress(consumer);
const encodedProviders = encodeAddressArray(providers);
const encodedBytes = encodedUser + encodedConsumer + encodedProviders;
// Calculate the length of the payload (in bytes)
const payloadLength = encodedBytes.length / 2;
const payloadLengthHex = payloadLength.toString(16).padStart(4, '0');
// Combine all parts
return (constants_1.DIVVI_MAGIC_PREFIX +
constants_1.REFERRAL_TAG_FORMAT_1_BYTE +
payloadLengthHex +
encodedBytes);
}
/**
* Posts an attribution event to the tracking API
*
* @param params - The parameters for the attribution event
* @param params.txHash - The transaction hash (for transaction-based referrals)
* @param params.message - The signed message (for signed message-based referrals, can be string or Hex)
* @param params.signature - The signature of the message (for signed message-based referrals)
* @param params.chainId - The chain ID
* @param params.baseUrl - The base URL for the API endpoint (optional)
* @returns A promise that resolves to the response from the API
* @throws {Error} Client error (4xx) - When the request fails due to client-side issues
* @throws {Error} Server error (5xx) - When the request fails due to server-side issues, client should retry the request
*/
async function submitReferral(params) {
const { chainId, baseUrl = 'https://api.divvi.xyz/submitReferral' } = params;
let body;
if ('txHash' in params) {
// Transaction-based referral
body = {
txHash: params.txHash,
chainId,
};
}
else {
// Signed message-based referral
body = {
message: params.message,
signature: params.signature,
chainId,
};
}
const headers = {
'Content-Type': 'application/json',
};
if (params.divviApiKey) {
headers['X-Divvi-Api-Key'] = params.divviApiKey;
}
const response = await fetch(baseUrl, {
method: 'POST',
headers,
body: JSON.stringify(body),
});
if (!response.ok) {
// Handle 4xx client errors
if (response.status >= 400 && response.status < 500) {
const errorResponse = await response.text();
throw new Error(`Client error: ${response.status} ${response.statusText} ${errorResponse}`);
}
// Handle all other errors (5xx server errors, etc.)
throw new Error(`Server error: Failed to submit referral event: ${response.statusText}. Client should retry the request.`);
}
return response;
}
// Re-export only the types that are needed for the public API
var types_2 = require("./types");
Object.defineProperty(exports, "InvalidAddressError", { enumerable: true, get: function () { return types_2.InvalidAddressError; } });
//# sourceMappingURL=index.js.map