A linguagem GCULpy

A GCULpy é um subconjunto estrito de tipagem estática do Python, projetado para ser seguro, legível e auditável. O design dela limita intencionalmente certos recursos dinâmicos do Python para evitar vulnerabilidades comuns de contratos inteligentes e garantir que o comportamento de um contrato seja sempre previsível.

Nesta página, apresentamos uma referência para a especificação da linguagem GCULpy, que abrange os conceitos principais e o ciclo de vida de um contrato em uma rede de Universal Ledger.

Principais conceitos

O contrato a seguir define um exemplo de token ERC20 em GCULpy:

import gcul

class ERC20Token(gcul.Contract):
    """Sample ERC20 implementation for the Universal Ledger."""

    symbol: str
    total_supply: int
    balance: gcul.AccountField[int]

    def __init__(self, symbol: str):
        self.symbol = symbol
        self.total_supply = 0

    def mint(self, beneficiary: gcul.Account, value: int) -> int:
        """Mints tokens to the given beneficiary."""
        assert self.is_owner(gcul.sender), "Only the owner can mint"
        assert value >= 0, "Mint amount must be non-negative"
        self.total_supply += value
        self.balance[beneficiary] += value
        return value

    def transfer(self, beneficiary: gcul.Account, value: int) -> int:
        """Transfers tokens from the sender to the given beneficiary."""
        assert value >= 0, "Transfer amount must be non-negative"
        assert (
            value <= self.balance[gcul.sender]
        ), "Sender does not have enough balance"
        self.balance[gcul.sender] -= value
        self.balance[beneficiary] += value
        return value

Um contrato GCULpy é uma classe que herda de gcul.Contract. Ela contém campos (estado de armazenamento) e métodos (lógica de processamento que opera em campos).

Campos

O estado em um contrato GCULpy é armazenado em campos. Todos os campos precisam ser declarados com um tipo estático no nível da classe. Há dois tipos de campos:

  • Os campos de contrato contêm um único valor armazenado com o próprio contrato. No exemplo ERC20Token, symbol: str e total_supply: int são campos de contrato.

  • Os campos de conta têm um valor separado para cada conta que interage com o contrato. Eles são declarados com gcul.AccountField[T], em que T é o tipo do valor por conta. No exemplo ERC20Token, balance: gcul.AccountField[int] é um campo de conta. Antes que um contrato possa gravar no espaço de armazenamento reservado para essa conta, a permissão de armazenamento precisa ser explicitamente concedida ao contrato. Depois que os dados são armazenados, apenas a instância do contrato pode modificá-los, não a conta.

Os campos de restrição têm os seguintes limites de armazenamento. Uma transação que exceda qualquer um dos limites falhará.

  • Todos os campos de um contrato compartilham uma única entrada de armazenamento, que pode conter cerca de 5 MiB de dados serializados no total.
  • Todos os campos de conta que um contrato armazena para cada conta individual podem conter no máximo 1 MiB de dados serializados no total.

Métodos

Os métodos definem a lógica executável de um contrato. Eles se comportam como métodos do Python e podem ler ou modificar os campos do contrato.

  • __init__: o construtor é chamado apenas uma vez quando o contrato é implantado pela primeira vez. Cada campo de contrato precisa receber um valor no construtor. O construtor não inicializa os campos da conta. O valor de cada conta é criado quando ela concede a permissão de armazenamento.

  • Métodos particulares: os métodos que começam com um sublinhado (por exemplo, _internal_logic) são particulares e só podem ser chamados por outros métodos no mesmo contrato. O interpretador do Universal Ledger aplica essa restrição.

  • Métodos públicos: qualquer método que não comece com um sublinhado (_) é público. Os métodos públicos podem ser chamados por qualquer usuário com o ROLE_CONTRACT_PARTICIPANT enviando uma transação InvokeContractMethod.

Restrição: os métodos públicos e todos os métodos @gcul.readonly não podem usar tipos mutáveis nas assinaturas, como list ou dict. Em vez disso, use declarações de tipo somente leitura, como Sequence ou Mapping.

Restrição: durante a execução, os métodos são limitados a um limite superior de uso de computação em toda a rede. Exceder esse limite resulta em uma falha de transação.

Ciclo de vida do contrato

As seções a seguir mostram as operações típicas envolvidas no ciclo de vida de um contrato do GCULpy.

Implantar um contrato

Primeiro, compile o código-fonte do GCULpy usando o compilador gculpyc. Em seguida, um usuário com o ROLE_CONTRACT_CREATOR pode enviar uma transação CreateContract para implantar o bytecode compilado em uma rede do Universal Ledger. Para instruções detalhadas, consulte o tutorial Implantar um contrato programável.

Uma transação desse tipo seria assim:

client_transaction {
  sender_id: "OWNER_ACCOUNT_ID"
  create_contract_transaction {
    contract_bytes: "COMPILED_BYTECODE"
    init_arguments {
      key: "symbol"
      value: { string_value: "US02079K1079" }
    }
  }
}

Quando a rede processa essa transação:

  • O construtor, que é o método __init__, é executado com os argumentos fornecidos para criar uma nova instância de contrato.
  • O remetente da transação se torna o proprietário do contrato.
  • A instância do contrato é armazenada permanentemente no livro-razão e recebe um ID de contrato exclusivo, que é retornado como parte da saída da transação.

Conceder permissões

Antes que um contrato possa armazenar dados em um campo de conta em nome de uma conta, seja uma conta de usuário ou outro contrato, a permissão de armazenamento precisa ser concedida ao contrato que armazena os dados. Essa é uma etapa de segurança crítica.

Como conceder permissões para uma conta de usuário

Um usuário com um ROLE_CONTRACT_PARTICIPANT pode enviar uma transação GrantContractPermissions para um ID de contrato específico:

client_transaction {
  sender_id: "PARTICIPANT_ACCOUNT_ID"
  grant_contract_permissions_transaction {
    contract_id: "CONTRACT_ID"
    permissions: CONTRACT_PERMISSION_STORAGE
  }
}

Quando a rede processa essa transação:

  • Se o contrato não definir nenhum campo de conta, a transação será rejeitada.
  • Se o contrato definir campos de conta, todos eles serão preenchidos com valores padrão (por exemplo, contract.balance[gcul.sender] = 0). Esses valores serão armazenados no estado global como parte dos dados da conta.

Como conceder permissões para um contrato de delegado

Um usuário com um ROLE_CONTRACT_PARTICIPANT pode enviar uma transação GrantContractPermissions para permitir que um contrato armazene dados em nome de outro contrato que ele possui:

client_transaction {
  sender_id: "OWNER_ACCOUNT_ID"
  grant_contract_permissions_transaction {
    contract_id: "TARGET_CONTRACT_ID"
    delegate_contract_id: "DELEGATE_CONTRACT_ID"
    permissions: CONTRACT_PERMISSION_STORAGE
  }
}

Quando a rede processa essa transação:

  • O remetente da transação, ou seja, sender_id, precisa ser o proprietário do contrato identificado por delegate_contract_id.
  • Um contrato não pode conceder permissões de armazenamento a si mesmo. Portanto, delegate_contract_id precisa ser diferente de contract_id.
  • As permissões de armazenamento são concedidas exclusivamente ao contrato especificado em delegate_contract_id, e não ao remetente da transação.
  • Os campos de conta definidos no contrato de destino, contract_id, são inicializados para o contrato delegado, delegate_contract_id, permitindo que os métodos no contrato de destino acessem com segurança os campos de conta indexados por gcul.caller ou gcul.sender durante invocações entre contratos.

Invocar métodos de contrato

Depois que um contrato é implantado e as permissões necessárias são concedidas, os usuários podem interagir com ele chamando os métodos públicos. Um usuário com um ROLE_CONTRACT_PARTICIPANT pode enviar uma transação InvokeContractMethod especificando o ID do contrato, o nome do método e os valores do argumento do método.

client_transaction {
  sender_id: "PARTICIPANT_ACCOUNT_ID"
  invoke_contract_method_transaction {
    contract_id: "CONTRACT_ID"
    method_name: "mint"
    method_arguments {
      key: "beneficiary"
      value: { account_id: "BENEFICIARY_ID" }
    }
    method_arguments {
      key: "value"
      value: { int64_value: 10 }
    }
  }
}

Quando a rede processa essa transação:

  • A instância de contrato associada ao CONTRACT_ID fornecido é recuperada.
  • O método mint(beneficiary=Account("BENEFICIARY_ID"), value=10) é executado. O objeto Account do beneficiário é criado e validado pelo ambiente de execução. A lógica do método pode presumir com segurança que o ID fornecido é válido e se refere a uma conta existente no livro.
  • Se o método falhar por qualquer motivo, a transação vai falhar e nenhuma atualização será feita no estado do contrato.
  • Se o método for bem-sucedido, o estado atualizado do contrato será registrado no estado global.

Especificação de linguagem

A GCULpy foi projetada para segurança e previsibilidade e, como tal, não permite vários recursos do Python. Eles serão chamados com o rótulo Restrição. Essas restrições são recursos permanentes da linguagem, introduzidos para facilitar a leitura, auditoria e análise estática da lógica do contrato, limitando comportamentos surpreendentes ou não seguros.

Outros recursos são identificados com o rótulo Roadmap. Eles estão no roteiro de implementação, mas ainda não são compatíveis com o compilador gculpyc.

Tipos

O GCULpy é compatível com uma variedade de tipos de variáveis comuns, com uma forte ênfase na tipagem estática.

Tipos de valores principais:

  • int, bool, str e None já são compatíveis.
  • Roteiro Decimal, bytes, Enum estão no roteiro.
  • Restrição: float e complex não são permitidos.
  • Restrição: os valores int são números inteiros assinados de 64 bits, de -263 a 263 - 1. Um literal de número inteiro fora desse intervalo resultará em um erro de tempo de compilação, e uma operação com resultado fora desse intervalo resultará em um erro de tempo de execução.
  • Restrição: os valores de str podem ter no máximo 1.000.000 de caracteres. Uma string literal mais longa vai resultar em um erro de tempo de compilação. Transmitir uma string mais longa como um argumento de método ou produzir uma como resultado de uma operação resultará em um erro de tempo de execução.

Tipos de contêiner:

  • list e dict:
    • list[T]: uma sequência mutável. Oferece suporte à indexação, por exemplo, my_list[0], e ao método append().
    • dict[K, V]: um mapeamento mutável. Oferece suporte à indexação, por exemplo, my_dict[key], e ao método pop().
  • collections.abc.Sequence e collections.abc.Mapping:
    • collections.abc.Sequence[T]: uma referência somente leitura a uma sequência. Oferece suporte à leitura de elementos por índice, por exemplo, my_seq[0], mas os elementos não podem ser modificados ou excluídos. list[T] é um subtipo de Sequence[T]. Portanto, é possível transmitir, retornar ou atribuir um valor ou literal de lista list[T] a um destino digitado como Sequence[T], mas não é possível atribuir um Sequence[T] a um list[T].
    • collections.abc.Mapping[K, V]: uma referência somente leitura a um mapeamento. Oferece suporte à leitura de entradas por chave, por exemplo, my_map[key], mas as entradas não podem ser modificadas ou excluídas. dict[K, V] é um subtipo de Mapping[K, V]. Portanto, é possível transmitir, retornar ou atribuir um valor dict[K, V] ou um literal de dicionário a um destino digitado como Mapping[K, V], mas não é possível atribuir um Mapping[K, V] a um dict[K, V].
  • Roteiro tuple, set, dataclass estão no roteiro.
  • Restrição: os tipos concretos precisam ser especificados para valores em um contêiner. Por exemplo, dict[str, int] é permitido, mas um dict ou dict[str, Any] simples não.
  • Roteiro: fatiamento de listas, por exemplo, my_list[1:3].
  • Roteiro Outros métodos, como .get(), .keys(), .values(), .items(), .clear() ou .extend().

Restrição: todas as variáveis, incluindo campos de contrato e conta, parâmetros de função e tipos de retorno, precisam ser definidas e digitadas estaticamente. O tipo deles não pode ser alterado no momento da execução, e apenas tipos concretos são aceitos. Os tipos não podem ser usados como valores. Por exemplo, eles não podem ser armazenados em variáveis ou transmitidos para funções como argumentos. Tentar atribuir um valor a um campo não declarado resultará em um erro de tempo de compilação.

Restrição: uma variável local precisa ser atribuída antes de ser lida. As variáveis locais não têm valor padrão. Portanto, ler uma delas antes de ser atribuída resulta em um erro de tempo de execução.

Classes e herança

Só é possível definir classes que sejam subclasses diretas de gcul.Contract ou gcul.ContractInterface. Essa regra estrita evita as complexidades da herança completa do Python, que pode introduzir bugs difíceis de encontrar e dificultar a compreensão do código. Por segurança, tentar substituir uma propriedade ou um método de uma classe pai vai gerar um erro, fornecendo uma proteção clara contra comportamentos inesperados.

Plano de lançamento: a GCULpy vai oferecer mais flexibilidade, mantendo os princípios fundamentais. O roteiro inclui suporte para herança única em classes definidas pelo usuário com substituição de método gerenciada explicitamente com um decorador @override. Além disso, o super() integrado só será compatível com o formato livre argumentos para garantir operações diretas e previsíveis.

O módulo gcul

A GCULpy oferece um módulo gcul integrado com tipos, decoradores e variáveis essenciais para o desenvolvimento de contratos.

classe gcul.Contract

A classe de base para todos os contratos. Não é possível criar instâncias dele diretamente. Os contratos só são instanciados por meio de transações CreateContract. Os métodos e as propriedades da classe de base gcul.Contract não podem ser substituídos em subclasses.

  • Contract.is_owner(account: Account) -> bool

    Retorna True se a conta fornecida for a proprietária do contrato.

  • Contract.call(target: ContractInterface, payment: Amount | None = None) -> ContractMethods

    Inicia uma interação entre contratos com target. Em vez de chamar métodos diretamente no destino (por exemplo, target.foo()), use self.call() para rotear explicitamente a invocação pelo ambiente de execução do ledger. Esse mecanismo retorna um objeto de proxy seguro usado para invocar métodos públicos correspondentes no contrato de destino, garantindo que os limites de execução sejam aplicados de forma estrita e que qualquer payment seja entregue com segurança. Consulte ContractInterface para mais detalhes.

    Um payment precisa ser fornecido ao chamar um método decorado com @gcul.payable e omitido para métodos não pagáveis.

    target precisa ser um contrato diferente. Chamar o contrato atual por meio de self.call() gera um erro de tempo de execução. Para chamar outro método no mesmo contrato, chame-o diretamente, por exemplo, self.foo().

  • Contract.native_transfer(beneficiary: Account, amount: Amount) -> None

    Transfere um valor no livro diretamente do saldo do contrato para beneficiary.

  • Contract.id: str

    ID de string exclusivo do contrato.

classe gcul.Account

Um tipo integrado que representa uma conta de usuário ou de contrato no livro-razão. Não é possível criar objetos gcul.Account diretamente. O ambiente de execução os cria para você e os fornece como argumentos de função ou método. Para transmitir uma conta como um argumento de transação, forneça um AccountValue que contenha o ID da conta. O ambiente de execução valida o ID e o converte em um objeto de conta completo. Se o ID não pertencer a uma conta registrada, a transação vai falhar. Isso garante que você só trabalhe com contas válidas.

A definição da classe é mais ou menos equivalente a:

@dataclasses.dataclass(frozen=True)
class Account:
  """A valid account on the ledger."""

  id: str  # The ID of the account as a string.
  • Account.id: str

    ID de string exclusivo da conta.

classe gcul.Amount

Representa um valor monetário denominado em uma moeda de contabilidade específica. Instancie um valor usando gcul.Amount(value: int, currency: gcul.Currency). A definição da classe é aproximadamente equivalente a:

@dataclasses.dataclass(frozen=True)
class Amount:
  """Represents an amount denominated in a ledger currency."""

  value: int
  currency: Currency

classe gcul.Currency

Representa uma moeda totalmente qualificada no livro-razão. A definição da classe é aproximadamente equivalente a:

@dataclasses.dataclass(frozen=True)
class Currency:
  """Represents a fully qualified currency on the ledger."""

  code: str
  operator_id: str

classe gcul.ContractInterface

A classe de base para declarar a interface pública de um contrato. Os métodos e as propriedades da classe de base gcul.ContractInterface não podem ser sobrepostos em subclasses. Além disso, os métodos na declaração da interface não podem incluir nenhum código, apenas ... é permitido.

Para que um contrato chame métodos em outro contrato implantado no livro, declare uma classe de interface que herde de gcul.ContractInterface. Os stubs de método precisam corresponder à declaração correspondente no contrato de destino. Isso inclui argumentos, tipo de retorno e anotações de decorador, como @gcul.readonly ou @gcul.payable.

No código do contrato, use self.call(target, payment) para invocar um método de outro contrato.

O exemplo a seguir ilustra como um contrato TokenVendor interage com outro contrato que implementa o ERC20Interface. Ele usa self.call() para transferir tokens para um comprador em troca de um pagamento em moeda no livro:

class ERC20Interface(gcul.ContractInterface):
  """Interface for an external ERC20 token contract."""

  def transfer(self, beneficiary: gcul.Account, value: int) -> int:
    ...

class TokenVendor(gcul.Contract):
  """A vendor contract that distributes ERC20 tokens."""

  price_per_token: int

  def __init__(self, price_per_token: int) -> None:
    self.price_per_token = price_per_token

  @gcul.payable
  def purchase_tokens(self, token: ERC20Interface, quantity: int) -> int:
    """Accepts on-ledger currency for a specific quantity of ERC20 tokens."""

    # Enforce positive quantity.
    assert quantity > 0, "Quantity must be positive"

    # Calculate the required payment.
    required_payment = quantity * self.price_per_token
    assert gcul.payment.value == required_payment, "Incorrect payment amount"

    # Invokes `transfer` on the external ERC20 token contract
    # to move tokens from the vendor's own balance to the caller.
    return self.call(token).transfer(gcul.caller, quantity)

Os tipos de interface também podem ser usados como campos de contrato, não apenas como parâmetros de método. Armazenar uma interface em um campo permite que um contrato mantenha uma referência a outro contrato em todas as transações e substitua essa referência mais tarde. O exemplo de contrato a seguir usa o ERC20Interface declarado anteriormente:

class Router(gcul.Contract):
  """Holds a replaceable reference to an ERC20 token contract."""

  target: ERC20Interface

  def __init__(self, target: ERC20Interface) -> None:
    self.target = target

  def set_target(self, new_target: ERC20Interface) -> None:
    assert self.is_owner(gcul.sender), "Only the owner can set the target"
    self.target = new_target

classe gcul.AccountField

Um tipo genérico integrado para declarar campos de conta. gcul.AccountField[T] contém um valor separado do tipo T para cada conta que concedeu ao contrato a permissão de armazenamento. Ele se comporta como um mapeamento com chaves gcul.Account, que podem ser usadas para ler ou gravar um valor para cada conta específica. Exemplo:

class Token(gcul.Contract):
  balance: gcul.AccountField[int]

  def deposit(self, account: gcul.Account, value: int) -> None:
    self.balance[account] = value  # Writes the value for one account.

  def get_balance(self, account: gcul.Account) -> int:
    return self.balance[account]  # Reads the value for one account.

T precisa ser um tipo que tenha um valor padrão. Portanto, gcul.Account, gcul.Amount, gcul.Currency e tipos de interface não podem ser usados como valores de campo de conta. Quando uma conta concede a permissão de armazenamento a um contrato, todos os campos da conta são preenchidos com o valor padrão do tipo para essa conta.

Restrição: a indexação com uma conta é a única operação que um campo de conta aceita. O campo em si não pode ser atribuído a uma variável, transmitido como um argumento, retornado de uma função ou iterado.

Restrição: não é possível excluir as entradas do campo de conta. Um valor armazenado pode ser substituído, mas a entrada em si é permanente.

Restrição: os campos de conta para gcul.sender, gcul.caller e qualquer gcul.Account transmitido como um argumento de método (incluindo listas internas ou outros contêineres) são carregados antes da execução do método. Para outras contas, uma única transação pode carregar lentamente os campos de conta para no máximo 15 pares (contract, account) distintos. O acesso a um campo de conta para o mesmo par várias vezes em uma transação conta apenas uma vez. Uma transação que excede esse limite falha.

@gcul.readonly

Anota um método de contrato como somente leitura. As declarações de método com esse decorador não podem alterar o estado do contrato nem fazer transferências de moeda no livro contábil. Eles também não podem usar tipos de contêiner mutáveis nas assinaturas, por exemplo, list ou dict. Em vez disso, eles precisam usar declarações de tipo somente leitura, como Sequence ou Mapping.

@gcul.payable

Anota um método de contrato como exigindo um pagamento para ser executado. Se um método espera receber um pagamento em moeda on-ledger usando gcul.payment, ele precisa ser marcado com esse decorador. Caso contrário, a transação vai falhar.

gcul.sender: gcul.Account

Contém uma referência à conta que assinou e enviou a transação InvokeContractMethod atual. O valor de gcul.sender não muda durante a execução da transação, mesmo que ela envolva chamadas entre contratos, ao contrário de gcul.caller, que se refere ao autor da chamada imediato.

gcul.caller: gcul.Account

Contém uma referência ao autor da chamada imediato do método atual. Para transações diretas de usuários, gcul.caller corresponde a gcul.sender, que aponta para quem enviou a transação. Para chamadas entre contratos, gcul.caller corresponde ao ID da conta do contrato de chamada. As chamadas internas em um contrato não alteram o valor de gcul.caller.

gcul.payment: gcul.Amount

Mantém o pagamento transferido para um método decorado com @gcul.payable. Acessar gcul.payment dentro de um método não pagável gera um erro de tempo de compilação.

Operadores

A maioria dos operadores disponíveis em Python é compatível com o GCULpy e funciona conforme o esperado.

  • Adição (+) e subtração (-), incluindo formas unárias e binárias.
  • Multiplicação (*), divisão de piso (//) e módulo (%).
  • Exponenciação (**) para expoentes positivos.
  • Comparações (<, <=, >, >=, ==, !=).
  • Bit a bit e (&), ou (|), xor (^), deslocamento para a esquerda (<<), deslocamento para a direita (>>), negação (~).
  • Operações booleanas (and, or, not).
  • Planejamento Identidade do objeto (is).
  • Restrição: expoentes negativos não são permitidos e geram um erro de tempo de execução.
  • Restrição: a divisão verdadeira (/), por ter um tipo de retorno float, não é permitida e resulta em um erro de tempo de compilação.

Fluxo de controle

A maioria das instruções de fluxo de controle do Python funciona no GCULpy com a mesma semântica:

  • pass.
  • chamadas de função interna (mesmo contrato, não recursivas).
  • assert.
  • if ... then .. else ....
  • for VAR in CONTAINER.
  • chamadas de função externa (para qualquer outro contrato, não recorrente).
  • Instruções break e continue.
  • raise.
  • Declarações de roteiro try ... except.
  • Declarações de roteiro match.
  • Instruções generators e yield do roteiro.
  • Gerenciadores de contexto do Roadmap e instruções with.

Restrição: o GCULpy é intencionalmente Turing-incompleto para evitar loops infinitos e facilitar a análise estática. Veja como ele faz isso:

  • Sem loops infinitos: a iteração só é permitida usando loops for em contêineres finitos ou sequências limitadas com range(...). Loops while não são permitidos. Algumas atualizações de contêiner, como adicionar ou remover elementos de uma lista ou chaves de um dicionário, não são permitidas durante a iteração.
  • Sem recursão: uma função não pode chamar a si mesma, direta ou indiretamente. O ambiente de execução realiza verificações estáticas e de execução para detectar e rejeitar o uso de recursão.
  • Sem fluxo de controle assíncrono: o uso de primitivos async não é permitido para manter os princípios básicos de design de previsibilidade, segurança e execução determinística. As operações assíncronas dificultam o raciocínio sobre o fluxo de controle de um programa, o que geralmente leva a vulnerabilidades e disputas.

Funções integradas.

O GCULpy é compatível com as seguintes funções integradas:

  • abs(x)

    Retorna o valor absoluto de um número inteiro x. Os argumentos precisam ser do tipo int.

  • len(s)

    Retorna o comprimento, ou seja, o número de itens, de uma string ou de um contêiner finito.

  • max(iterable)
    max(arg1, arg2, *args)

    Retorna o maior item em um iterável ou o maior de dois ou mais argumentos. Chamar max() em um iterável vazio gera um erro de tempo de execução.

  • min(iterable)
    min(arg1, arg2, *args)

    Retorna o menor item em um iterável ou o menor de dois ou mais argumentos. Chamar min() em um iterável vazio gera um erro de tempo de execução.

  • range(stop)
    range(start, stop[, step=1])

    Gera uma sequência aritmética de números inteiros com um número inteiro diferente de zero step.

    Restrição: só pode ser usado inline com loops for, por exemplo, for i in range(10): .... Os objetos de intervalo não podem ser atribuídos a variáveis ou armazenados em campos, transmitidos como argumentos para funções ou consultados com len().

Roteiro As seguintes funções integradas estão no roteiro de implementação:

A
all()
any()

B
bin()
bool()
bytes()

C
chr()

D
dict()
divmod()

E
enumerate()

F
format()
frozenset()

H
hash()
hex()

I
id()
int()

L
list()

O
oct()
ord()

P
pow()
property()

R
repr()
reversed()

S
set()
sorted()
staticmethod()
str()
sum()
super()

T
tuple()

Z
zip()

Notas de lançamento

  • 3 de novembro de 2026. Lançamos a versão em disponibilidade geral do compilador gculpyc (v0.2.0), incluindo suporte para chamadas entre contratos, transferências e pagamentos de moedas no livro, métodos @gcul.readonly, list, outras instruções de fluxo de controle e funções integradas.

  • 28 de janeiro de 2026. Versão inicial do compilador gculpyc disponibilizada para os participantes do pré-lançamento particular do Universal Ledger. Para ver um tutorial sobre como usar o compilador, consulte Implantar um contrato programável.