Skip to main content

OpenAI writes ERC20 in Odra

· 4 min read

OpenAI can write Odra smart contracts. This is how.

OpenAI

OpenAI already proved that AI can code. Github Copilot is used by more and more developers. Many times it is mind-blowing how accurate it is. It would be great if one of the OpenAI models could simply work after writing:

Q: Write the ERC20 smart contract.

A: use odra::{Mapping, Variable}...

So far OpenAI hasn't indexed Odra. I even asked ChatGPT.

Q: Do you know what is the Odra Framework for writing smart contracts?
Response in one sentence. Use Yoda style.

A: Aware of an Odra Framework for writing smart contracts, I am not.

Soon (year or two) it will happen and Odra will be supported out of the box, simply because it is available on GitHub.

DaVinci Edit

OpenAI gives us a great tool called Edit. It uses the same technology Github Copiled it based on called Codex, OpenAI gives us access to the model code-davinci-edit-001. It allows you to paste in the code and ask for changes. See more examples like rewriting Fibonacci to a recursive version here.

But it doesn't know Odra. Luckily this is not a problem. I have found a nice trick that allows producing the code I want. We simply have to present all the features of Odra and ask a good question.

ERC20 by AI

The goal is to bend AI to write the ERC20 token using Odra. The Edit query needs two elements:

  • source code,
  • text that describes what should be changed.

As the input source code I wrote the code that uses all the features ERC20 would use. I need odra::module, Variable, Mapping, caller(), Address, constructor, and errors.

flipper.rs
use odra::{Variable, types::Address, contract_env, Mapping, execution_error};

#[odra::module]
pub struct Flipper {
value: Variable<bool>,
flips_count: Variable<u32>,
owner: Variable<Address>,
messages: Mapping<(Address, Address), String>
}

#[odra::module]
impl Flipper {
#[odra(init)]
pub fn init(&mut self, owner: Address) {
self.value.set(false);
self.owner.set(owner);
}

pub fn set(&mut self, value: bool) {
self.value.set(value);
}

pub fn flip(&mut self) {
self.value.set(!self.get_value());
self.flips_count.set(self.flips_count.get_or_default());
}

pub fn get_value(&self) -> bool {
self.value.get_or_default()
}

pub fn get_owner(&self) -> Option<Address> {
self.owner.get()
}

pub fn set_message(&mut self, recipient: Address, msg: String) {
let sender = contract_env::caller();
let current_msg = self.messages.get(&(sender, recipient));
match current_msg {
Some(_) => contract_env::revert(Error::MessageAlreadyDefined),
None => self.messages.set(&(sender, recipient), msg)
}
}

pub fn get_message(&self, sender: Address, recipient: Address) -> String {
self.messages.get_or_default(&(sender, recipient))
}
}

execution_error! {
pub enum Error {
MessageAlreadyDefined => 1,
NotAnMessageSender => 2,
}
}

ChatGPT knows what the ERC20 is, so the kind request for changes is simple.

Q: Implement another smart contract called ERC20.
Balances should be of type U256.

This is the result.

ERC20 contract in Odra generated by OpenAI
use odra::{Variable, types::Address, contract_env, Mapping, execution_error, U256};

#[odra::module]
pub struct ERC20 {
name: Variable<String>,
symbol: Variable<String>,
decimals: Variable<u8>,
total_supply: Variable<U256>,
balances: Mapping<Address, U256>,
allowed: Mapping<(Address, Address), U256>
}

#[odra::module]
impl ERC20 {
#[odra(init)]
pub fn init(&mut self, name: String, symbol: String, decimals: u8, total_supply: U256) {
self.name.set(name);
self.symbol.set(symbol);
self.decimals.set(decimals);
self.total_supply.set(total_supply);
self.balances.set(&contract_env::caller(), total_supply);
}

pub fn total_supply(&self) -> U256 {
self.total_supply.get_or_default()
}

pub fn balance_of(&self, owner: Address) -> U256 {
self.balances.get_or_default(&owner)
}

pub fn transfer(&mut self, to: Address, value: U256) {
let from = contract_env::caller();
let from_balance = self.balances.get_or_default(&from);
let to_balance = self.balances.get_or_default(&to);
if from_balance < value {
contract_env::revert(Error::InsufficientBalance);
}
self.balances.set(&from, from_balance - value);
self.balances.set(&to, to_balance + value);
}

pub fn transfer_from(&mut self, from: Address, to: Address, value: U256) {
let caller = contract_env::caller();
let from_balance = self.balances.get_or_default(&from);
let to_balance = self.balances.get_or_default(&to);
let allowed = self.allowed.get_or_default(&(from, caller));
if from_balance < value {
contract_env::revert(Error::InsufficientBalance);
}
if allowed < value {
contract_env::revert(Error::InsufficientAllowance);
}
self.balances.set(&from, from_balance - value);
self.balances.set(&to, to_balance + value);
self.allowed.set(&(from, caller), allowed - value);
}

pub fn approve(&mut self, spender: Address, value: U256) {
let owner = contract_env::caller();
self.allowed.set(&(owner, spender), value);
}

pub fn allowance(&self, owner: Address, spender: Address) -> U256 {
self.allowed.get_or_default(&(owner, spender))
}
}

execution_error! {
pub enum Error {
InsufficientBalance => 1,
InsufficientAllowance => 2,
}
}

Wow!

What is next?

AI already understands smart contracts. It had to read a lot of Solidity code. I checked and it knows Uniswap and Compound source code. Most interesting is that AI was able to implement a concept it already knew from another programming language in the unseen Rust framework. At some point, we will be able to translate all the Solidity code to Odra. Or maybe we will not have to...

It is hard to imagine how good it will be in 2025 and beyond.