Genesis Contracts
@rgbjs/* packages don't existThe @rgbjs/... imports on this page refer to npm packages that were never published, and the @rgbjs scope isn't registered. Treat these examples as illustrative only and don't install anything under that name: anyone could register it. Real JavaScript options: Line B (RGB Protocol Association) has the rgb-lib bindings from RGB-Tools. Line A (RGB-WG v0.12) has no JavaScript SDK. This page is being rewritten.
The genesis is the birth of an RGB contract - it defines the initial state, allocates ownership, and anchors the contract to Bitcoin. This guide covers creating, deploying, and verifying RGB contract genesis with detailed implementation examples from rgb-core and rgb-std.
What is Genesis?​
Genesis is the first operation of an RGB contract, analogous to a genesis block in blockchain systems. It establishes the contract's initial conditions and serves as the immutable root of all future state transitions.
Genesis Responsibilities​
- Contract Identity: Genesis defines the unique contract ID through its commitment hash
- Initial Global State: Sets immutable and mutable contract-wide parameters
- Initial Allocations: Distributes owned state to initial holders via blinded seals
- Schema Binding: Links the contract to its schema for validation rules
- Bitcoin Anchoring: Commits to Bitcoin for timestamping and publication
Once created and published, genesis is immutable and cryptographically binds all contract properties.
Genesis Structure​
Core Components​
Genesis consists of several key components as implemented in rgb-std:
// From rgb-std GenesisSchema
pub struct GenesisSchema {
// Metadata types allowed in genesis
metadata: TinyOrdMap<MetaType, Occurrences>,
// Global state requirements
globals: TinyOrdMap<GlobalStateType, Occurrences>,
// Owned state (assignments) requirements
assignments: TinyOrdMap<OwnedStateType, Occurrences>,
// Valencies (contract extension points)
valencies: TinyOrdMap<ValencyType, Occurrences>,
// AluVM validation script for genesis
validator: Option<LibSite>,
}
State Types in Genesis​
Global State - Contract-wide data visible to all parties:
- Immutable fields (ticker, name, precision)
- Mutable fields (total supply, metadata)
- Stored as key-value pairs in strict types
Owned State - Value attached to specific seals (UTXOs):
- Fungible amounts (RGB20 tokens)
- Data attachments (RGB21 NFTs)
- Rights (issuance, admin permissions)
Metadata - Optional descriptive information:
- Contract documentation
- Media attachments
- Ricardian contracts
Genesis Data Structure​
interface Genesis {
// Schema reference
schema_id: SchemaId;
// Contract identity and timestamp
contract_id: ContractId;
timestamp: number;
// Global state initialization
globals: {
[field_name: string]: StateValue;
};
// Owned state allocations
assignments: {
[state_type: string]: Assignment[];
};
// Optional metadata
metadata: {
[meta_type: string]: MetaValue;
};
// Validation script reference
script: LibSite;
}
interface Assignment {
// Blinded UTXO seal
seal: BlindedSeal;
// State data (amount, token data, etc.)
state: StateData;
// Witness for verification
witness: WitnessData;
}
Creating Genesis - RGB20 Example​
Step 1: Define Asset Parameters​
The NIA (Non-Inflatable Asset) schema implements RGB20:
// From rgb-schemata/src/nia.rs
pub fn testnet(
issuer: &str,
ticker: &str,
name: &str,
details: Option<&str>,
precision: Precision,
allocations: impl IntoIterator<Item = (Method, impl TxOutpoint, Amount)>,
) -> Result<ValidContract, InvalidRString>
Example parameters:
const assetSpec = {
ticker: 'USDT', // 1-8 ASCII characters
name: 'USD Tether', // Asset name
precision: 8, // Decimal places (0-18)
details: 'Stablecoin pegged to USD', // Optional description
};
const terms = {
text: ricardianContract, // Legal terms
media: attachmentHash, // Optional contract document
};
const issuedSupply = 1_000_000_00000000n; // 1 million with 8 decimals
Step 2: Create Blinded Seals​
Seals hide the actual UTXO while allowing verification:
import { BlindSeal, CloseMethod } from '@rgbjs/core';
// Create blinded seal for recipient
const recipientSeal = BlindSeal.with_blinding(
CloseMethod.TapretFirst, // Taproot commitment method
txid, // Bitcoin transaction ID
vout, // Output index
blindingSecret // Random blinding factor
);
// For multiple allocations
const allocations = [
{
method: 'TapretFirst',
beneficiary: seal1,
amount: 600_000_00000000n
},
{
method: 'TapretFirst',
beneficiary: seal2,
amount: 400_000_00000000n
}
];
Step 3: Build Genesis with Schema​
// From rgb-schemata examples
import { NonInflatableAsset } from 'rgb-schemata';
const contract = NonInflatableAsset.testnet(
'ssi:anonymous', // Issuer identity
'USDT', // Ticker
'USD Tether', // Name
'Stablecoin pegged to USD', // Details
Precision.CentiMicro, // 8 decimal places
allocations // Initial distribution
);
const contractId = contract.contract_id();
console.log('Contract ID:', contractId.toString());
Step 4: Genesis Global State​
The NIA schema requires specific global state fields:
// From rgb-schemata/src/nia.rs
global_types: tiny_bmap! {
GS_NOMINAL => GlobalStateSchema::once(types.get("RGBContract.AssetSpec")),
GS_TERMS => GlobalStateSchema::once(types.get("RGBContract.ContractTerms")),
GS_ISSUED_SUPPLY => GlobalStateSchema::once(types.get("RGBContract.Amount")),
}
Implementation:
const builder = ContractBuilder
.new(schema, issuer)
// Required: Asset specification
.add_global_state("spec", {
ticker: "USDT",
name: "USD Tether",
precision: 8,
details: null
})
// Required: Contract terms
.add_global_state("terms", {
text: ricardianContract,
media: null
})
// Required: Issued supply
.add_global_state("issuedSupply", 1_000_000_00000000n);
Step 5: Add Owned State Assignments​
// From rgb-schemata/src/nia.rs
owned_types: tiny_bmap! {
OS_ASSET => OwnedStateSchema::Fungible(FungibleType::Unsigned64Bit),
}
genesis: GenesisSchema {
assignments: tiny_bmap! {
OS_ASSET => Occurrences::OnceOrMore,
},
// ...
}
Adding fungible allocations:
// Deterministic allocation with specific blinding factor
builder.add_fungible_state_det(
"assetOwner", // State type name
seal, // Blinded seal
1_000_000_00000000n, // Amount
blindingFactor // Specific blinding (optional)
);
// Or use random blinding
builder.add_fungible_state(
"assetOwner",
seal,
1_000_000_00000000n
);
Step 6: Issue Contract​
// Issue with current timestamp
const contract = builder.issue_contract();
// Or deterministic issue with specific timestamp
const contract = builder.issue_contract_det(timestamp);
// Contract is now ready for validation and anchoring
console.log('Contract issued:', contract.contract_id());
Creating Genesis - RGB21 Example​
NFT Genesis Structure​
The UDA (Unique Digital Asset) schema implements RGB21:
// From rgb-schemata/src/uda.rs
global_types: tiny_bmap! {
GS_NOMINAL => GlobalStateSchema::once(types.get("RGBContract.AssetSpec")),
GS_TERMS => GlobalStateSchema::once(types.get("RGBContract.ContractTerms")),
GS_TOKENS => GlobalStateSchema::once(types.get("RGB21.TokenData")),
GS_ATTACH => GlobalStateSchema::once(types.get("RGB21.AttachmentType")),
}
owned_types: tiny_bmap! {
OS_ASSET => OwnedStateSchema::Structured(types.get("RGBContract.Allocation")),
}
Building RGB21 Genesis​
import { UniqueDigitalAsset } from 'rgb-schemata';
// Define token data
const tokenData = {
index: 1n, // Token ID
ticker: null, // Optional ticker
name: 'Genesis NFT', // Token name
details: 'First NFT', // Description
preview: { // Embedded preview
type: 'image/png',
data: previewImageBytes
},
media: attachmentHash, // Full media hash
attachments: [], // Additional attachments
reserves: null // Future use
};
// Create allocation (NFTs use structured state)
const allocation = {
index: 1n, // Same as token index
fraction: 1n // Always 1 for unique assets
};
// Build genesis
const contract = stock.contract_builder(
'ssi:anonymous',
UniqueDigitalAsset.schema().schema_id(),
'RGB21Unique'
)
.add_global_state('spec', {
ticker: 'NFT',
name: 'My NFT Collection',
precision: 0 // Indivisible
})
.add_global_state('terms', contractTerms)
.add_global_state('tokens', tokenData)
.add_data('assetOwner', beneficiarySeal, allocation)
.issue_contract();
Genesis Validation​
AluVM Validation Scripts​
Genesis validation is performed by AluVM scripts embedded in the schema:
// From rgb-schemata/src/nia.rs - NIA Genesis Validator
pub(crate) fn nia_lib() -> Lib {
let code = rgbasm! {
// SUBROUTINE Genesis validation
// Checking pedersen commitments against reported amount
put a8[0],ERRNO_ISSUED_MISMATCH;
put a8[1],0;
put a16[0],0;
// Read global state into s16[0]
ldg GS_ISSUED_SUPPLY,a8[1],s16[0];
// Extract 64 bits from the beginning of s16[0] into a64[0]
extr s16[0],a64[0],a16[0];
// Verify sum of pedersen commitments for assignments against a64[0] value
pcas OS_ASSET;
test;
ret;
};
Lib::assemble::<Instr<RgbIsa<MemContract>>>(&code)
.expect("wrong non-inflatable asset script")
}
What Genesis Validation Checks​
- Schema Compliance: All required fields present
- Type Validity: Values match declared types
- Pedersen Commitment: Blinded amounts sum correctly
- Occurrence Rules: Field occurrences match schema (Once, OnceOrMore, etc.)
- Custom Logic: Schema-specific validation rules
Validation Example​
// Genesis validation happens automatically during issue
try {
const contract = builder.issue_contract();
console.log('✓ Genesis validation passed');
console.log('Contract ID:', contract.contract_id());
} catch (error) {
if (error.code === 'ERRNO_ISSUED_MISMATCH') {
console.error('✗ Issued supply mismatch with commitments');
} else if (error.code === 'SCHEMA_VIOLATION') {
console.error('✗ Schema validation failed:', error.details);
} else {
console.error('✗ Genesis validation failed:', error);
}
}
Bitcoin Anchoring​
Commitment Process​
RGB uses deterministic Bitcoin commitments (DBC) to anchor contracts:
// 1. Create Bitcoin transaction
const bitcoinTx = createBitcoinTransaction({
inputs: [myUtxo],
outputs: [
{ address: changeAddress, amount: changeAmount }
]
});
// 2. Add RGB commitment
const commitment = contract.create_commitment();
const committedTx = bitcoinTx.add_opreturn_commitment(commitment);
// 3. Sign and broadcast
const signedTx = wallet.sign(committedTx);
const txid = await broadcast(signedTx);
console.log('Genesis anchored in:', txid);
Commitment Structure​
Bitcoin Transaction Output:
OP_RETURN
<protocol_tag> # 'rgb' or protocol identifier
<commitment_hash> # SHA256 of contract data
<extra_proof> # Optional: multi-protocol proof
Taproot Commitment (Preferred Method)​
Modern RGB uses Taproot for better privacy:
// TapretFirst - First taproot output
const seal = BlindSeal.with_blinding(
CloseMethod.TapretFirst,
txid,
0, // First taproot output
blindingSecret
);
// The commitment is embedded in the taproot script tree
// Much more efficient and private than OP_RETURN
PSBT Construction​
For hardware wallets and multi-sig:
import { Psbt } from 'bitcoinjs-lib';
// 1. Create PSBT with RGB commitment
const psbt = new Psbt({ network });
psbt.addInput({
hash: utxo.txid,
index: utxo.vout,
witnessUtxo: {
script: utxo.scriptPubKey,
value: utxo.value
}
});
// 2. Add output with RGB commitment
psbt.addOutput({
script: createCommitmentScript(commitment),
value: 0 // OP_RETURN has zero value
});
// 3. Sign
psbt.signInput(0, keyPair);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
Verification Process​
Client-Side Validation​
Each party independently validates the genesis:
class GenesisValidator {
async validate(genesis: Genesis): Promise<ValidationResult> {
const errors = [];
// 1. Schema validation
const schemaCheck = await this.validateSchema(genesis);
if (!schemaCheck.valid) {
errors.push(...schemaCheck.errors);
}
// 2. Global state validation
const globalCheck = await this.validateGlobalState(genesis);
if (!globalCheck.valid) {
errors.push(...globalCheck.errors);
}
// 3. Owned state validation
const ownedCheck = await this.validateOwnedState(genesis);
if (!ownedCheck.valid) {
errors.push(...ownedCheck.errors);
}
// 4. Run AluVM validator
const scriptCheck = await this.runValidator(genesis);
if (!scriptCheck.valid) {
errors.push(...scriptCheck.errors);
}
// 5. Verify Bitcoin anchor
const anchorCheck = await this.validateAnchor(genesis);
if (!anchorCheck.valid) {
errors.push(...anchorCheck.errors);
}
return {
valid: errors.length === 0,
errors,
contractId: genesis.contract_id()
};
}
}
Pedersen Commitment Verification​
For fungible assets, amounts are hidden using Pedersen commitments:
// From NIA validation script
// pcas (Pedersen Commitment Asset Sum)
// Verifies: sum(commitment_i) == commitment(declared_amount)
pcas OS_ASSET; // Validate asset commitments
test; // Fail if invalid
This ensures:
- Total supply matches allocated amounts
- Individual amounts stay private
- No inflation possible
Real-World Examples​
Example 1: Simple Token Genesis​
import { NonInflatableAsset } from 'rgb-schemata';
// Create 1 million tokens
const contract = NonInflatableAsset.testnet(
'ssi:anonymous',
'TEST',
'Test Token',
null, // No details
8, // 8 decimals
[
['TapretFirst', beneficiaryUtxo, 1_000_000_00000000n]
]
);
// Save contract
contract.save_file('test-token.rgb');
contract.save_armored('test-token.rgba'); // ASCII-armored version
Example 2: Multi-Allocation Genesis​
const allocations = [
// Team allocation - 40%
['TapretFirst', teamSeal, 400_000_00000000n],
// Community sale - 30%
['TapretFirst', saleSeal, 300_000_00000000n],
// Reserve - 20%
['TapretFirst', reserveSeal, 200_000_00000000n],
// Liquidity - 10%
['TapretFirst', liquiditySeal, 100_000_00000000n]
];
const contract = NonInflatableAsset.testnet(
'ssi:project_org',
'PROJ',
'Project Token',
'Governance token for Project DAO',
8,
allocations
);
Example 3: Deterministic Genesis​
For reproducible contract IDs:
import { ContractBuilder, BlindingFactor } from 'rgb-std';
const builder = ContractBuilder.deterministic(
Identity.default(),
schema,
iface,
impl,
types,
scripts
)
.add_global_state('spec', spec)
.add_global_state('terms', terms)
.add_global_state('issuedSupply', supply)
.add_fungible_state_det(
'assetOwner',
seal,
amount,
BlindingFactor.from_hex('a3401bcc...') // Fixed blinding
);
// Always produces same contract ID with same inputs
const contract = builder.issue_contract_det(timestamp);
Error Handling​
Common Genesis Errors​
// Error codes from rgb-schemata
const ERRNO_ISSUED_MISMATCH = 1; // Supply ≠sum(allocations)
const ERRNO_NON_EQUAL_IN_OUT = 2; // Conservation violation
const ERRNO_NON_FRACTIONAL = 3; // Fractional NFT (invalid)
Handling Validation Failures​
try {
const contract = builder.issue_contract();
} catch (error) {
switch (error.errno) {
case ERRNO_ISSUED_MISMATCH:
console.error('Total supply does not match allocations');
console.error('Declared:', declaredSupply);
console.error('Allocated:', sum(allocations));
break;
case 'MISSING_REQUIRED_FIELD':
console.error('Required field missing:', error.field);
console.error('Schema requires:', schema.genesis.globals);
break;
case 'TYPE_MISMATCH':
console.error('Type validation failed:', error.details);
break;
default:
console.error('Genesis validation failed:', error);
}
}
Recovery Strategies​
async function createGenesisWithRetry(params) {
let attempt = 0;
const maxAttempts = 3;
while (attempt < maxAttempts) {
try {
// Validate allocations sum to total supply
const totalAllocated = params.allocations.reduce(
(sum, a) => sum + a.amount, 0n
);
if (totalAllocated !== params.supply) {
throw new Error(`Supply mismatch: ${params.supply} vs ${totalAllocated}`);
}
// Create genesis
const contract = await NonInflatableAsset.testnet(
params.issuer,
params.ticker,
params.name,
params.details,
params.precision,
params.allocations
);
return contract;
} catch (error) {
attempt++;
console.warn(`Genesis attempt ${attempt} failed:`, error);
if (attempt >= maxAttempts) {
throw error;
}
// Adjust parameters based on error
if (error.errno === ERRNO_ISSUED_MISMATCH) {
// Recalculate allocations
params = adjustAllocations(params);
}
}
}
}
Testing Genesis​
Local Testing with Regtest​
# Start Bitcoin regtest
bitcoind -regtest -daemon
# Create test wallet
bitcoin-cli -regtest createwallet "rgb-test"
# Generate blocks to get coins
bitcoin-cli -regtest -generate 101
# Create UTXO for RGB anchor
ADDR=$(bitcoin-cli -regtest getnewaddress)
UTXO=$(bitcoin-cli -regtest sendtoaddress $ADDR 0.001)
# Mine block
bitcoin-cli -regtest -generate 1
Genesis Testing Script​
import { NonInflatableAsset } from 'rgb-schemata';
import { BitcoinRegtest } from 'bitcoin-regtest';
async function testGenesis() {
const regtest = new BitcoinRegtest();
await regtest.start();
// Get test UTXO
const utxo = await regtest.getFreshUtxo();
// Create test contract
const contract = NonInflatableAsset.testnet(
'ssi:test',
'TEST',
'Test Token',
null,
8,
[['TapretFirst', utxo, 1000_00000000n]]
);
console.log('Genesis created:', contract.contract_id());
// Validate
const validator = new GenesisValidator();
const result = await validator.validate(contract);
if (result.valid) {
console.log('✓ Genesis validation passed');
} else {
console.error('✗ Genesis validation failed:');
result.errors.forEach(e => console.error(' -', e));
}
await regtest.stop();
}
Unit Testing​
import { describe, it, expect } from '@jest/globals';
describe('Genesis Creation', () => {
it('should create valid RGB20 genesis', () => {
const contract = NonInflatableAsset.testnet(
'ssi:test',
'TEST',
'Test',
null,
8,
[['TapretFirst', testUtxo, 1000n]]
);
expect(contract.contract_id()).toBeDefined();
});
it('should reject mismatched supply', () => {
expect(() => {
// This should fail validation
builder
.add_global_state('issuedSupply', 1000n)
.add_fungible_state('assetOwner', seal, 900n) // Wrong!
.issue_contract();
}).toThrow(ERRNO_ISSUED_MISMATCH);
});
it('should produce deterministic contract ID', () => {
const contract1 = createDeterministicGenesis(params);
const contract2 = createDeterministicGenesis(params);
expect(contract1.contract_id()).toEqual(contract2.contract_id());
});
});
Production Deployment​
Pre-Deployment Checklist​
- Schema validated and tested
- All required global state fields populated
- Allocations sum to total supply (RGB20)
- Blinded seals generated securely
- Ricardian contract reviewed
- Genesis validated locally
- Testnet deployment successful
- Bitcoin anchor UTXO prepared
- Backup of genesis data secured
- Recovery procedures documented
Mainnet Deployment​
import { NonInflatableAsset } from 'rgb-schemata';
import { BitcoinMainnet } from '@rgbjs/bitcoin';
async function deployMainnet() {
// 1. Prepare secure randomness for blinding
const blindingSecret = crypto.randomBytes(32);
// 2. Get fresh Bitcoin UTXO
const bitcoinWallet = new BitcoinMainnet();
const anchorUtxo = await bitcoinWallet.getUtxo({
minValue: 10000, // 10k sats
confirmations: 6
});
// 3. Create production genesis
const contract = NonInflatableAsset.testnet( // Note: method name is testnet but works for mainnet
ISSUER_IDENTITY,
TICKER,
NAME,
DESCRIPTION,
PRECISION,
[
['TapretFirst', seal1, allocation1],
['TapretFirst', seal2, allocation2],
// ... more allocations
]
);
// 4. Save multiple backups
await contract.save_file(`${TICKER}-genesis.rgb`);
await contract.save_armored(`${TICKER}-genesis.rgba`);
await backupToSecureStorage(contract);
// 5. Publish
const consignment = await contract.create_consignment();
await publishConsignment(consignment);
console.log('✓ Contract deployed:', contract.contract_id());
return contract;
}
Security Considerations​
- Blinding Factor Security: Use cryptographically secure random for blinding
- Private Key Management: Secure keys controlling anchor UTXOs
- Genesis Backup: Store multiple encrypted backups
- Consignment Publication: Use redundant publication channels
- Timestamp Verification: Confirm Bitcoin anchor is confirmed
Advanced Patterns​
Multi-Contract Genesis (Batch Issuance)​
// Issue multiple contracts in parallel
const contracts = await Promise.all([
createTokenGenesis('USDT', 'USD Tether', ...),
createTokenGenesis('USDC', 'USD Coin', ...),
createTokenGenesis('DAI', 'Dai Stablecoin', ...)
]);
// All can share same Bitcoin anchor
const sharedAnchor = await createAnchorTx();
await Promise.all(
contracts.map(c => c.anchor(sharedAnchor))
);
Genesis with Future Issuance Rights​
// Reserve issuance rights for future token creation
const allocations = [
// Initial distribution
['TapretFirst', publicSaleSeal, 600_000_00000000n],
// Issuer retains issuance rights
// (Implementation depends on schema supporting secondary issuance)
];
Time-Locked Genesis​
// Genesis that activates at specific block height
const genesis = builder
.add_global_state('activationHeight', 850000)
.add_validation_rule(
'current_height() >= global.activationHeight'
)
.issue_contract();
Related Documentation​
- RGB Schemas - Understanding contract schemas
- State Transitions - How to modify contract state
- Contractum Language - High-level contract definition
- RGB20 Token Creation - Fungible token guide
- RGB21 NFT Creation - NFT creation guide
- Bitcoin Integration - UTXO and anchoring
- Client-Side Validation - Validation principles
- Consignments - State transfer format