Skip to content

Atomic NFTs make it possible to sign and own pieces of the public decentralized data commons in the form of unique, universally identifiable digital assets.

Notifications You must be signed in to change notification settings

atomic-nfts/standard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Atomic NFTs

The official docs portal can be found on atomicnft.com.

An Atomic NFT uses Arweave Transaction Meta Data to generate a Smart Contract and store a media file in a single transaction.

This unique standard makes a new type of NFT that is:

  • Eco-friendly (contracts are lazily executed, and proof of work is used minimally)
  • Contracts and media are intertwined and cannot be separated
  • Media and contract data use a common locator
  • Proofs of Real traffic provide attention rewards via Koii
  • Can be bridged to any blockchain network

Go to narcissus to check out the example about how to deploy a NFT.

Contributing

Contribute to atomic-nfts and make it better, here's the tutorial to make a good, clean pull request.

How to make a clean pull request

  • First, go to atomicnft.com.
  • Go to the page you want to make it better.
  • Click the Suggest edits on Github.
  • Edit the file.(There's a pencil icon)
  • After you finish, write the description and choose "Create a new branch". Give it a clear name.
  • Click Propose changes.

The new branch will be created and you are done!

General Structure

The standard contract for Atomic NFTs can be found here: r_ibeOTHJW8McJvivPJjHxjMwkYfAKRjs-LjAeaBcLc

The State Object

The standard NFT has this common structure:

{
  "owner": "ay1uavTv9SVRKVIrUhH8178ON_zjAsZo2tcm5wiC4bI",
  "title": "Missing piece",
  "name": "Kirthivasan",
  "description": "nothing makes a room emptier than wanting someone in it.\nspread love",
  "ticker": "KOINFT",
  "balances": {
    "ay1uavTv9SVRKVIrUhH8178ON_zjAsZo2tcm5wiC4bI": 1
  },
  "contentType": "image/jpeg",
  "createdAt": "1626023631",
  "locked":[]
}

Contract Source

A standard contract might look like this:

export function handle(state, action) {
  const input = action.input;
  const caller = action.caller;
  if (input.function === "transfer") {
    const target = input.target;
    ContractAssert(target, `No target specified.`);
    ContractAssert(caller !== target, `Invalid token transfer.`);
    const qty = input.qty;
    ContractAssert(qty && qty > 0, `No valid quantity specified.`);
    const balances = state.balances;
    ContractAssert(
      caller in balances && balances[caller] >= qty,
      `Caller has insufficient funds`
    );
    balances[caller] -= qty;
    if (!(target in balances)) {
      balances[target] = 0;
    }
    balances[target] += qty;
    state.balances = balances;
    return { state };
  }
  if (input.function === "balance") {
    let target;
    if (input.target) {
      target = input.target;
    } else {
      target = caller;
    }
    const ticker = state.ticker;
    const balances = state.balances;
    ContractAssert(
      typeof target === "string",
      `Must specify target to retrieve balance for.`
    );
    return {
      result: {
        target,
        ticker,
        balance: target in balances ? balances[target] : 0
      }
    };
  }

  if (input.function === "lock") {
    const delegatedOwner = input.delegatedOwner;
    ContractAssert(delegatedOwner, `No target specified.`);
    const qty = input.qty;
    ContractAssert(qty && qty > 0, `No valid quantity specified.`);
    const balances = state.balances;
    ContractAssert(
      caller in balances && balances[caller] >= qty,
      `Caller has insufficient funds`
    );
    const address = input.address;
    const network = input.network;
    ContractAssert(address, `No target specified.`);
    ContractAssert(network, `No network specified.`);
    balances[caller] -= qty;
    let lockedArray = state.locked;
    let index = lockedArray.findIndex((e) => {
      e.vaultAddress == delegatedOwner && e.lockedBy == caller;
    });
    if (index >= 0) {
      lockedArray[index].amount += qty;
    } else {
      lockedArray.push({
        UID: SmartWeave.transaction.id,
        vaultAddress: delegatedOwner,
        lockedBy: caller,
        amount: qty,
        address: address,
        network: network
      });
    }
    state.locked = lockedArray;
    return { state };
  }
  // Only the vault owner can call this function
  if (input.function === "unlock") {
    const recipientAddress = input.recipientAddress;
    ContractAssert(recipientAddress, `No target specified.`);
    let qty = input.qty;
    ContractAssert(qty && qty > 0, `No valid quantity specified.`);
    let lockedArray = state.locked;
    let index = lockedArray.findIndex((e) => {
      return e.vaultAddress == caller;
    });
    ContractAssert(
      index >= 0,
      `Only vault owner can call this function and there must be some locked NFTs under the recipient address`
    );
    if (!state.balances[recipientAddress]) state.balances[recipientAddress] = 0;
    if (lockedArray[index].amount - qty == 0) {
      lockedArray.splice(index, 1);
      state.balances[recipientAddress] =
        Number(state.balances[recipientAddress]) + qty;
    } else if (lockedArray[index].amount - qty > 0) {
      lockedArray[index].amount -= qty;
      state.balances[recipientAddress] =
        Number(state.balances[recipientAddress]) + qty;
    } else {
      ContractAssert(
        lockedArray[index].amount - qty >= 0,
        `You cannot unlock more qty than currently locked`
      );
    }
    state.locked = lockedArray;
    return { state };
  }

  throw new ContractError(
    `No function supplied or function not recognised: "${input.function}".`
  );
}

Bridging and Locking

The 'Lock' and 'Unlock' methods contain function for delegating ownership of an NFT when bridging to another chain.

Lock

The section below shows an example of a possible lock function:

export default function lock(state, action) {
  const input = action.input;
  const caller = action.caller;
  const delegatedOwner = input.delegatedOwner;
  ContractAssert(delegatedOwner, `No target specified.`);
  const qty = input.qty;
  ContractAssert(qty, `No quantity specified.`);
  const balances = state.balances;
  ContractAssert(
    caller in balances && balances[caller] >= qty,
    `Caller has insufficient funds`
  );
  balances[caller] -= qty;
  if (!(delegatedOwner in balances)) {
    balances[delegatedOwner] = 0;
  }
  balances[delegatedOwner] += qty;

  const ethOwnerAddress = input.ethOwnerAddress;
  ContractAssert(ethOwnerAddress, `No ethereum address specified.`);
  state.ethOwnerAddress = ethOwnerAddress;
  return { state };
}

Unlock

The 'Unlock' method handles removing an NFT from custodianship.

The section below shows an example of a possible unlock function:

export default function unlock(state, action) {
  const input = action.input;
  const balances = state.balances;
  const addresses = Object.keys(balances);
  for (const address of addresses) {
    delete balances[address];
  }

  const qty = input.qty;
  ContractAssert(qty, `No quantity specified.`);
  const arweaveAddress = input.arweaveAddress;
  ContractAssert(arweaveAddress, `No arweaveAddress specified.`);
  if (!(arweaveAddress in balances)) {
    balances[arweaveAddress] = 0;
  }
  balances[arweaveAddress] += qty;
  delete state.ethOwnerAddress;

  return { state };
}

For more information about bridge compatibility, contact developers@koii.network.

About

Atomic NFTs make it possible to sign and own pieces of the public decentralized data commons in the form of unique, universally identifiable digital assets.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages